innodb_ruby 0.9.14 → 0.12.0
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.
- checksums.yaml +7 -0
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +654 -778
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -325
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -276
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +196 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +965 -924
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/page.rb +417 -414
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +381 -413
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- data/lib/innodb.rb +4 -5
- metadata +100 -25
data/lib/innodb/undo_log.rb
CHANGED
@@ -1,134 +1,156 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Innodb
|
4
|
+
class UndoLog
|
5
|
+
Header = Struct.new(
|
6
|
+
:trx_id,
|
7
|
+
:trx_no,
|
8
|
+
:delete_mark_flag,
|
9
|
+
:log_start_offset,
|
10
|
+
:xid_flag,
|
11
|
+
:ddl_flag,
|
12
|
+
:ddl_table_id,
|
13
|
+
:next_log_offset,
|
14
|
+
:prev_log_offset,
|
15
|
+
:history_list_node,
|
16
|
+
:xid,
|
17
|
+
keyword_init: true
|
18
|
+
)
|
19
|
+
|
20
|
+
HeaderXid = Struct.new(
|
21
|
+
:format,
|
22
|
+
:trid_len,
|
23
|
+
:bqual_len,
|
24
|
+
:data,
|
25
|
+
keyword_init: true
|
26
|
+
)
|
27
|
+
|
28
|
+
attr_reader :page
|
29
|
+
attr_reader :position
|
30
|
+
|
31
|
+
def initialize(page, position)
|
32
|
+
@page = page
|
33
|
+
@position = position
|
34
|
+
end
|
14
35
|
|
15
|
-
|
16
|
-
|
17
|
-
|
36
|
+
def size_xa_header
|
37
|
+
4 + 4 + 4 + 128
|
38
|
+
end
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
xid_flag = nil
|
22
|
-
{
|
23
|
-
:trx_id => c.name("trx_id") { c.get_uint64 },
|
24
|
-
:trx_no => c.name("trx_no") { c.get_uint64 },
|
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
|
-
:history_list_node => c.name("history_list_node") {
|
33
|
-
Innodb::List.get_node(c)
|
34
|
-
},
|
35
|
-
:xid => c.name("xid") {
|
36
|
-
if xid_flag
|
37
|
-
{
|
38
|
-
:format => c.name("format") { c.get_uint32 },
|
39
|
-
:trid_len => c.name("trid_len") { c.get_uint32 },
|
40
|
-
:bqual_len => c.name("bqual_len") { c.get_uint32 },
|
41
|
-
:data => c.name("data") { c.get_bytes(128) },
|
42
|
-
}
|
43
|
-
end
|
44
|
-
},
|
45
|
-
}
|
40
|
+
def size_header
|
41
|
+
8 + 8 + 2 + 2 + 1 + 1 + 8 + 2 + 2 + Innodb::List::NODE_SIZE + size_xa_header
|
46
42
|
end
|
47
|
-
end
|
48
43
|
|
49
|
-
|
50
|
-
|
51
|
-
|
44
|
+
def header
|
45
|
+
@header ||= page.cursor(@position).name("header") do |c|
|
46
|
+
header = Header.new(
|
47
|
+
trx_id: c.name("trx_id") { c.read_uint64 },
|
48
|
+
trx_no: c.name("trx_no") { c.read_uint64 },
|
49
|
+
delete_mark_flag: c.name("delete_mark_flag") { (c.read_uint16 != 0) },
|
50
|
+
log_start_offset: c.name("log_start_offset") { c.read_uint16 },
|
51
|
+
xid_flag: c.name("xid_flag") { (c.read_uint8 != 0) },
|
52
|
+
ddl_flag: c.name("ddl_flag") { (c.read_uint8 != 0) },
|
53
|
+
ddl_table_id: c.name("ddl_table_id") { c.read_uint64 },
|
54
|
+
next_log_offset: c.name("next_log_offset") { c.read_uint16 },
|
55
|
+
prev_log_offset: c.name("prev_log_offset") { c.read_uint16 },
|
56
|
+
history_list_node: c.name("history_list_node") { Innodb::List.get_node(c) }
|
57
|
+
)
|
58
|
+
|
59
|
+
if header.xid_flag
|
60
|
+
header.xid = c.name("xid") do
|
61
|
+
HeaderXid.new(
|
62
|
+
format: c.name("format") { c.read_uint32 },
|
63
|
+
trid_len: c.name("trid_len") { c.read_uint32 },
|
64
|
+
bqual_len: c.name("bqual_len") { c.read_uint32 },
|
65
|
+
data: c.name("data") { c.read_bytes(128) }
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
52
69
|
|
53
|
-
|
54
|
-
|
55
|
-
|
70
|
+
header
|
71
|
+
end
|
72
|
+
end
|
56
73
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
new_undo_record
|
61
|
-
end
|
74
|
+
def prev_address
|
75
|
+
header.history_list_node.prev
|
76
|
+
end
|
62
77
|
|
63
|
-
|
64
|
-
|
65
|
-
|
78
|
+
def next_address
|
79
|
+
header.history_list_node.next
|
80
|
+
end
|
66
81
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
+
def undo_record(offset)
|
83
|
+
new_undo_record = Innodb::UndoRecord.new(page, offset)
|
84
|
+
new_undo_record.undo_log = self
|
85
|
+
new_undo_record
|
82
86
|
end
|
83
87
|
|
84
|
-
def
|
85
|
-
|
86
|
-
@undo_record = rec
|
87
|
-
end
|
88
|
+
def min_undo_record
|
89
|
+
undo_record(header.log_start_offset)
|
88
90
|
end
|
89
91
|
|
90
|
-
|
91
|
-
|
92
|
-
@
|
92
|
+
class UndoRecordCursor
|
93
|
+
def initialize(undo_log, offset, direction = :forward)
|
94
|
+
@initial = true
|
95
|
+
@undo_log = undo_log
|
96
|
+
@offset = offset
|
97
|
+
@direction = direction
|
98
|
+
|
99
|
+
case offset
|
100
|
+
when :min
|
101
|
+
@undo_record = @undo_log.min_undo_record
|
102
|
+
when :max
|
103
|
+
raise "Not implemented"
|
104
|
+
else
|
105
|
+
@undo_record = @undo_log.undo_record(offset)
|
106
|
+
end
|
93
107
|
end
|
94
|
-
end
|
95
108
|
|
96
|
-
|
97
|
-
|
98
|
-
@
|
99
|
-
return @undo_record
|
109
|
+
def next_undo_record
|
110
|
+
rec = @undo_record.next
|
111
|
+
@undo_record = rec if rec
|
100
112
|
end
|
101
113
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
when :backward
|
106
|
-
prev_undo_record
|
114
|
+
def prev_undo_record
|
115
|
+
rec = @undo_record.prev
|
116
|
+
@undo_record = rec if rec
|
107
117
|
end
|
108
|
-
end
|
109
118
|
|
110
|
-
|
111
|
-
|
112
|
-
|
119
|
+
def undo_record
|
120
|
+
if @initial
|
121
|
+
@initial = false
|
122
|
+
return @undo_record
|
123
|
+
end
|
124
|
+
|
125
|
+
case @direction
|
126
|
+
when :forward
|
127
|
+
next_undo_record
|
128
|
+
when :backward
|
129
|
+
prev_undo_record
|
130
|
+
end
|
113
131
|
end
|
114
132
|
|
115
|
-
|
116
|
-
|
133
|
+
def each_undo_record
|
134
|
+
return enum_for(:each_undo_record) unless block_given?
|
135
|
+
|
136
|
+
while (rec = undo_record)
|
137
|
+
yield rec
|
138
|
+
end
|
117
139
|
end
|
118
140
|
end
|
119
|
-
end
|
120
141
|
|
121
|
-
|
122
|
-
|
123
|
-
|
142
|
+
def undo_record_cursor(offset, direction = :forward)
|
143
|
+
UndoRecordCursor.new(self, offset, direction)
|
144
|
+
end
|
124
145
|
|
125
|
-
|
126
|
-
|
127
|
-
|
146
|
+
def first_undo_record_cursor
|
147
|
+
undo_record_cursor(header.log_start_offset)
|
148
|
+
end
|
128
149
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
150
|
+
def dump
|
151
|
+
puts "header:"
|
152
|
+
pp header
|
153
|
+
puts
|
154
|
+
end
|
133
155
|
end
|
134
156
|
end
|
data/lib/innodb/undo_record.rb
CHANGED
@@ -1,165 +1,204 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
2
4
|
|
3
5
|
# A single undo log record.
|
4
|
-
|
5
|
-
|
6
|
-
|
6
|
+
module Innodb
|
7
|
+
class UndoRecord
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
Header = Struct.new(
|
11
|
+
:prev,
|
12
|
+
:next,
|
13
|
+
:type,
|
14
|
+
:extern_flag,
|
15
|
+
:info,
|
16
|
+
keyword_init: true
|
17
|
+
)
|
18
|
+
|
19
|
+
HeaderInfo = Struct.new(
|
20
|
+
:order_may_change,
|
21
|
+
:size_may_change,
|
22
|
+
keyword_init: true
|
23
|
+
)
|
24
|
+
|
25
|
+
Record = Struct.new(
|
26
|
+
:page,
|
27
|
+
:offset,
|
28
|
+
:header,
|
29
|
+
:undo_no,
|
30
|
+
:table_id,
|
31
|
+
:info_bits,
|
32
|
+
:trx_id,
|
33
|
+
:roll_ptr,
|
34
|
+
:data,
|
35
|
+
:key,
|
36
|
+
:row,
|
37
|
+
keyword_init: true
|
38
|
+
)
|
39
|
+
|
40
|
+
Field = Struct.new(
|
41
|
+
:name,
|
42
|
+
:type,
|
43
|
+
:value,
|
44
|
+
keyword_init: true
|
45
|
+
)
|
46
|
+
|
47
|
+
attr_reader :undo_page
|
48
|
+
attr_reader :position
|
49
|
+
|
50
|
+
attr_accessor :undo_log
|
51
|
+
attr_accessor :index_page
|
52
|
+
|
53
|
+
def initialize(undo_page, position)
|
54
|
+
@undo_page = undo_page
|
55
|
+
@position = position
|
56
|
+
|
57
|
+
@undo_log = nil
|
58
|
+
@index_page = nil
|
59
|
+
end
|
7
60
|
|
8
|
-
|
9
|
-
|
61
|
+
def new_subordinate(undo_page, position)
|
62
|
+
new_undo_record = self.class.new(undo_page, position)
|
63
|
+
new_undo_record.undo_log = undo_log
|
64
|
+
new_undo_record.index_page = index_page
|
10
65
|
|
11
|
-
|
12
|
-
|
13
|
-
@position = position
|
66
|
+
new_undo_record
|
67
|
+
end
|
14
68
|
|
15
|
-
|
16
|
-
|
17
|
-
|
69
|
+
# The header really starts 2 bytes before the undo record position, as the
|
70
|
+
# pointer to the previous record is written there.
|
71
|
+
def pos_header
|
72
|
+
@position - 2
|
73
|
+
end
|
18
74
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
75
|
+
# The size of the header.
|
76
|
+
def size_header
|
77
|
+
2 + 2 + 1
|
78
|
+
end
|
23
79
|
|
24
|
-
|
25
|
-
|
80
|
+
def pos_record
|
81
|
+
pos_header + size_header
|
82
|
+
end
|
26
83
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
84
|
+
# Return a BufferCursor starting before the header.
|
85
|
+
def cursor(position)
|
86
|
+
new_cursor = @undo_page.cursor(position)
|
87
|
+
new_cursor.push_name("undo_log[#{@undo_log.position}]") if @undo_log
|
88
|
+
new_cursor.push_name("undo_record[#{@position}]")
|
89
|
+
new_cursor
|
90
|
+
end
|
32
91
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
92
|
+
# Possible undo record types.
|
93
|
+
TYPE = {
|
94
|
+
11 => :insert,
|
95
|
+
12 => :update_existing,
|
96
|
+
13 => :update_deleted,
|
97
|
+
14 => :delete,
|
98
|
+
}.freeze
|
99
|
+
|
100
|
+
TYPES_WITH_PREVIOUS_VERSIONS = %i[
|
101
|
+
update_existing
|
102
|
+
update_deleted
|
103
|
+
delete
|
104
|
+
].freeze
|
105
|
+
|
106
|
+
TYPE_MASK = 0x0f
|
107
|
+
COMPILATION_INFO_MASK = 0x70
|
108
|
+
COMPILATION_INFO_SHIFT = 4
|
109
|
+
COMPILATION_INFO_NO_ORDER_CHANGE_BV = 1
|
110
|
+
COMPILATION_INFO_NO_SIZE_CHANGE_BV = 2
|
111
|
+
EXTERN_FLAG = 0x80
|
112
|
+
|
113
|
+
def header
|
114
|
+
@header ||= cursor(pos_header).name("header") do |c|
|
115
|
+
header = Header.new(
|
116
|
+
prev: c.name("prev") { c.read_uint16 },
|
117
|
+
next: c.name("next") { c.read_uint16 }
|
118
|
+
)
|
119
|
+
|
120
|
+
info = c.name("info") { c.read_uint8 }
|
121
|
+
cmpl = (info & COMPILATION_INFO_MASK) >> COMPILATION_INFO_SHIFT
|
122
|
+
header.type = TYPE[info & TYPE_MASK]
|
123
|
+
header.extern_flag = (info & EXTERN_FLAG) != 0
|
124
|
+
header.info = HeaderInfo.new(
|
125
|
+
order_may_change: (cmpl & COMPILATION_INFO_NO_ORDER_CHANGE_BV).zero?,
|
126
|
+
size_may_change: (cmpl & COMPILATION_INFO_NO_SIZE_CHANGE_BV).zero?
|
127
|
+
)
|
128
|
+
|
129
|
+
header
|
130
|
+
end
|
131
|
+
end
|
37
132
|
|
38
|
-
|
39
|
-
pos_header + size_header
|
40
|
-
end
|
133
|
+
def_delegator :header, :type
|
41
134
|
|
42
|
-
|
43
|
-
|
44
|
-
new_cursor = @undo_page.cursor(position)
|
45
|
-
if @undo_log
|
46
|
-
new_cursor.push_name("undo_log[#{@undo_log.position}]")
|
135
|
+
def previous_version?
|
136
|
+
TYPES_WITH_PREVIOUS_VERSIONS.include?(type)
|
47
137
|
end
|
48
|
-
new_cursor.push_name("undo_record[#{@position}]")
|
49
|
-
new_cursor
|
50
|
-
end
|
51
138
|
|
52
|
-
|
53
|
-
|
54
|
-
11 => :insert,
|
55
|
-
12 => :update_existing,
|
56
|
-
13 => :update_deleted,
|
57
|
-
14 => :delete,
|
58
|
-
}
|
59
|
-
|
60
|
-
TYPE_MASK = 0x0f
|
61
|
-
COMPILATION_INFO_MASK = 0x70
|
62
|
-
COMPILATION_INFO_SHIFT = 4
|
63
|
-
COMPILATION_INFO_NO_ORDER_CHANGE_BV = 1
|
64
|
-
COMPILATION_INFO_NO_SIZE_CHANGE_BV = 2
|
65
|
-
EXTERN_FLAG = 0x80
|
66
|
-
|
67
|
-
def header
|
68
|
-
@header ||= cursor(pos_header).name("header") do |c|
|
69
|
-
header = {
|
70
|
-
:prev => c.name("prev") { c.get_uint16 },
|
71
|
-
:next => c.name("next") { c.get_uint16 },
|
72
|
-
}
|
73
|
-
|
74
|
-
info = c.name("info") { c.get_uint8 }
|
75
|
-
cmpl = (info & COMPILATION_INFO_MASK) >> COMPILATION_INFO_SHIFT
|
76
|
-
header[:type] = TYPE[info & TYPE_MASK]
|
77
|
-
header[:extern_flag] = (info & EXTERN_FLAG) != 0
|
78
|
-
header[:info] = {
|
79
|
-
:order_may_change => (cmpl & COMPILATION_INFO_NO_ORDER_CHANGE_BV) == 0,
|
80
|
-
:size_may_change => (cmpl & COMPILATION_INFO_NO_SIZE_CHANGE_BV) == 0,
|
81
|
-
}
|
82
|
-
|
83
|
-
header
|
84
|
-
end
|
85
|
-
end
|
139
|
+
def get(prev_or_next)
|
140
|
+
return if header[prev_or_next].zero?
|
86
141
|
|
87
|
-
|
88
|
-
|
89
|
-
|
142
|
+
new_undo_record = new_subordinate(@undo_page, header[prev_or_next])
|
143
|
+
new_undo_record if new_undo_record.type
|
144
|
+
end
|
90
145
|
|
91
|
-
|
92
|
-
|
93
|
-
|
146
|
+
def prev
|
147
|
+
get(:prev)
|
148
|
+
end
|
94
149
|
|
95
|
-
|
96
|
-
|
97
|
-
new_undo_record = new_subordinate(@undo_page, header[prev_or_next])
|
98
|
-
if new_undo_record.type
|
99
|
-
new_undo_record
|
100
|
-
end
|
150
|
+
def next
|
151
|
+
get(:next)
|
101
152
|
end
|
102
|
-
end
|
103
153
|
|
104
|
-
|
105
|
-
|
106
|
-
|
154
|
+
def record_size
|
155
|
+
header[:next] - @position - size_header
|
156
|
+
end
|
107
157
|
|
108
|
-
|
109
|
-
|
110
|
-
|
158
|
+
def read_record
|
159
|
+
cursor(pos_record).name("record") do |c|
|
160
|
+
this_record = Record.new(
|
161
|
+
page: undo_page.offset,
|
162
|
+
offset: position,
|
163
|
+
header: header,
|
164
|
+
undo_no: c.name("undo_no") { c.read_imc_uint64 },
|
165
|
+
table_id: c.name("table_id") { c.read_imc_uint64 }
|
166
|
+
)
|
167
|
+
|
168
|
+
if previous_version?
|
169
|
+
this_record.info_bits = c.name("info_bits") { c.read_uint8 }
|
170
|
+
this_record.trx_id = c.name("trx_id") { c.read_ic_uint64 }
|
171
|
+
this_record.roll_ptr = c.name("roll_ptr") do
|
172
|
+
Innodb::DataType::RollPointerType.parse_roll_pointer(c.read_ic_uint64)
|
173
|
+
end
|
174
|
+
end
|
111
175
|
|
112
|
-
|
113
|
-
|
114
|
-
|
176
|
+
if index_page
|
177
|
+
read_record_fields(this_record, c)
|
178
|
+
else
|
179
|
+
# Slurp up the remaining data as a string.
|
180
|
+
this_record.data = c.read_bytes(header[:next] - c.position - 2)
|
181
|
+
end
|
115
182
|
|
116
|
-
|
117
|
-
cursor(pos_record).name("record") do |c|
|
118
|
-
this_record = {
|
119
|
-
:page => undo_page.offset,
|
120
|
-
:offset => position,
|
121
|
-
:header => header,
|
122
|
-
:undo_no => c.name("undo_no") { c.get_imc_uint64 },
|
123
|
-
:table_id => c.name("table_id") { c.get_imc_uint64 },
|
124
|
-
}
|
125
|
-
|
126
|
-
if has_previous_version?
|
127
|
-
this_record[:info_bits] = c.name("info_bits") { c.get_uint8 }
|
128
|
-
this_record[:trx_id] = c.name("trx_id") { c.get_ic_uint64 }
|
129
|
-
this_record[:roll_ptr] = c.name("roll_ptr") {
|
130
|
-
Innodb::DataType::RollPointerType.parse_roll_pointer(c.get_ic_uint64)
|
131
|
-
}
|
183
|
+
this_record
|
132
184
|
end
|
185
|
+
end
|
133
186
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
187
|
+
def read_record_fields(this_record, cursor)
|
188
|
+
this_record.key = []
|
189
|
+
index_page.record_format[:key].each do |field|
|
190
|
+
length = cursor.name("field_length") { cursor.read_ic_uint32 }
|
191
|
+
value = cursor.name(field.name) { field.value_by_length(cursor, length) }
|
140
192
|
|
141
|
-
|
142
|
-
|
143
|
-
end
|
193
|
+
this_record.key[field.position] = Field.new(name: field.name, type: field.data_type.name, value: value)
|
194
|
+
end
|
144
195
|
|
145
|
-
|
146
|
-
this_record[:key] = []
|
147
|
-
index_page.record_format[:key].each do |field|
|
148
|
-
this_record[:key][field.position] = {
|
149
|
-
:name => field.name,
|
150
|
-
:type => field.data_type.name,
|
151
|
-
:value => c.name(field.name) {
|
152
|
-
field_length = c.name("field_length") { c.get_ic_uint32 }
|
153
|
-
field.value_by_length(c, field_length)
|
154
|
-
}
|
155
|
-
}
|
156
|
-
end
|
196
|
+
return unless previous_version?
|
157
197
|
|
158
|
-
|
159
|
-
|
160
|
-
this_record[:row] = Array.new(index_page.record_format[:row].size)
|
198
|
+
field_count = cursor.name("field_count") { cursor.read_ic_uint32 }
|
199
|
+
this_record.row = Array.new(index_page.record_format[:row].size)
|
161
200
|
field_count.times do
|
162
|
-
field_number =
|
201
|
+
field_number = cursor.name("field_number[#{field_count}]") { cursor.read_ic_uint32 }
|
163
202
|
field = nil
|
164
203
|
field_index = nil
|
165
204
|
index_page.record_format[:row].each_with_index do |candidate_field, index|
|
@@ -168,140 +207,109 @@ class Innodb::UndoRecord
|
|
168
207
|
field_index = index
|
169
208
|
end
|
170
209
|
end
|
171
|
-
raise "Unknown field #{field_number}" unless field
|
172
|
-
this_record[:row][field_index] = {
|
173
|
-
:name => field.name,
|
174
|
-
:type => field.data_type.name,
|
175
|
-
:value => c.name(field.name) {
|
176
|
-
field_length = c.name("field_length") { c.get_ic_uint32 }
|
177
|
-
field.value_by_length(c, field_length)
|
178
|
-
}
|
179
|
-
}
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def undo_record
|
185
|
-
@undo_record ||= read_record
|
186
|
-
end
|
187
210
|
|
188
|
-
|
189
|
-
undo_record[:undo_no]
|
190
|
-
end
|
191
|
-
|
192
|
-
def table_id
|
193
|
-
undo_record[:table_id]
|
194
|
-
end
|
195
|
-
|
196
|
-
def trx_id
|
197
|
-
undo_record[:trx_id]
|
198
|
-
end
|
211
|
+
raise "Unknown field #{field_number}" unless field
|
199
212
|
|
200
|
-
|
201
|
-
|
202
|
-
end
|
213
|
+
length = cursor.name("field_length") { cursor.read_ic_uint32 }
|
214
|
+
value = cursor.name(field.name) { field.value_by_length(cursor, length) }
|
203
215
|
|
204
|
-
|
205
|
-
|
206
|
-
|
216
|
+
this_record.row[field_index] = Field.new(name: field.name, type: field.data_type.name, value: value)
|
217
|
+
end
|
218
|
+
end
|
207
219
|
|
208
|
-
|
209
|
-
|
210
|
-
|
220
|
+
def undo_record
|
221
|
+
@undo_record ||= read_record
|
222
|
+
end
|
211
223
|
|
212
|
-
|
213
|
-
undo_record
|
214
|
-
|
224
|
+
def_delegator :undo_record, :undo_no
|
225
|
+
def_delegator :undo_record, :table_id
|
226
|
+
def_delegator :undo_record, :trx_id
|
227
|
+
def_delegator :undo_record, :roll_ptr
|
228
|
+
def_delegator :undo_record, :key
|
229
|
+
def_delegator :undo_record, :page
|
230
|
+
def_delegator :undo_record, :offset
|
215
231
|
|
216
|
-
|
217
|
-
|
218
|
-
|
232
|
+
def key_string
|
233
|
+
key&.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }&.join(", ")
|
234
|
+
end
|
219
235
|
|
220
|
-
|
221
|
-
|
222
|
-
|
236
|
+
def row
|
237
|
+
undo_record[:row]
|
238
|
+
end
|
223
239
|
|
224
|
-
|
225
|
-
|
226
|
-
|
240
|
+
def row_string
|
241
|
+
row&.reject(&:nil?)&.map { |r| r && "%s=%s" % [r[:name], r[:value].inspect] }&.join(", ")
|
242
|
+
end
|
227
243
|
|
228
|
-
|
229
|
-
|
230
|
-
|
244
|
+
def string
|
245
|
+
"(%s) → (%s)" % [key_string, row_string]
|
246
|
+
end
|
231
247
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
unless has_previous_version?
|
248
|
+
# Find the previous row version by following the roll_ptr from one undo
|
249
|
+
# record to the next (backwards through the record version history). Since
|
250
|
+
# we are operating without the benefit of knowing about active transactions
|
251
|
+
# and without protection from purge, check that everything looks sane before
|
252
|
+
# returning it.
|
253
|
+
def prev_by_history
|
239
254
|
# This undo record type has no previous version information.
|
240
|
-
return
|
241
|
-
end
|
255
|
+
return unless previous_version?
|
242
256
|
|
243
|
-
|
244
|
-
|
257
|
+
undo_log = roll_ptr[:undo_log]
|
258
|
+
older_undo_page = @undo_page.space.page(undo_log[:page])
|
245
259
|
|
246
|
-
unless older_undo_page and older_undo_page.is_a?(Innodb::Page::UndoLog)
|
247
260
|
# The page was probably re-used for something else.
|
248
|
-
return
|
249
|
-
end
|
261
|
+
return unless older_undo_page.is_a?(Innodb::Page::UndoLog)
|
250
262
|
|
251
|
-
|
252
|
-
undo_log[:offset])
|
263
|
+
older_undo_record = new_subordinate(older_undo_page, undo_log[:offset])
|
253
264
|
|
254
|
-
unless older_undo_record and table_id == older_undo_record.table_id
|
255
265
|
# The record space was probably re-used for something else.
|
256
|
-
return
|
257
|
-
end
|
266
|
+
return unless older_undo_record && table_id == older_undo_record.table_id
|
258
267
|
|
259
|
-
unless older_undo_record.trx_id.nil? or trx_id >= older_undo_record.trx_id
|
260
268
|
# The trx_id should not be newer; but may be absent (for insert).
|
261
|
-
return nil
|
262
|
-
end
|
269
|
+
return unless older_undo_record.trx_id.nil? || trx_id >= older_undo_record.trx_id
|
263
270
|
|
264
|
-
|
265
|
-
end
|
266
|
-
|
267
|
-
def dump
|
268
|
-
puts "Undo record at offset %i" % offset
|
269
|
-
puts
|
270
|
-
|
271
|
-
puts "Header:"
|
272
|
-
puts " %-25s: %i" % ["Previous record offset", header[:prev]]
|
273
|
-
puts " %-25s: %i" % ["Next record offset", header[:next]]
|
274
|
-
puts " %-25s: %s" % ["Type", header[:type]]
|
275
|
-
puts
|
276
|
-
|
277
|
-
puts "System fields:"
|
278
|
-
puts " Transaction ID: %s" % trx_id
|
279
|
-
puts " Roll Pointer:"
|
280
|
-
puts " Undo Log: page %i, offset %i" % [
|
281
|
-
roll_ptr[:undo_log][:page],
|
282
|
-
roll_ptr[:undo_log][:offset],
|
283
|
-
]
|
284
|
-
puts " Rollback Segment ID: %i" % roll_ptr[:rseg_id]
|
285
|
-
puts
|
286
|
-
|
287
|
-
puts "Key fields:"
|
288
|
-
key.each do |field|
|
289
|
-
puts " %s: %s" % [
|
290
|
-
field[:name],
|
291
|
-
field[:value].inspect,
|
292
|
-
]
|
271
|
+
older_undo_record
|
293
272
|
end
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
puts "
|
300
|
-
|
301
|
-
|
273
|
+
|
274
|
+
def dump
|
275
|
+
puts "Undo record at offset %i" % offset
|
276
|
+
puts
|
277
|
+
|
278
|
+
puts "Header:"
|
279
|
+
puts " %-25s: %i" % ["Previous record offset", header[:prev]]
|
280
|
+
puts " %-25s: %i" % ["Next record offset", header[:next]]
|
281
|
+
puts " %-25s: %s" % ["Type", header[:type]]
|
282
|
+
puts
|
283
|
+
|
284
|
+
puts "System fields:"
|
285
|
+
puts " Transaction ID: %s" % trx_id
|
286
|
+
puts " Roll Pointer:"
|
287
|
+
puts " Undo Log: page %i, offset %i" % [
|
288
|
+
roll_ptr[:undo_log][:page],
|
289
|
+
roll_ptr[:undo_log][:offset],
|
302
290
|
]
|
291
|
+
puts " Rollback Segment ID: %i" % roll_ptr[:rseg_id]
|
292
|
+
puts
|
293
|
+
|
294
|
+
puts "Key fields:"
|
295
|
+
key.each do |field|
|
296
|
+
puts " %s: %s" % [
|
297
|
+
field[:name],
|
298
|
+
field[:value].inspect,
|
299
|
+
]
|
300
|
+
end
|
301
|
+
puts
|
302
|
+
|
303
|
+
puts "Non-key fields:"
|
304
|
+
row.each do |field|
|
305
|
+
next unless field
|
306
|
+
|
307
|
+
puts " %s: %s" % [
|
308
|
+
field[:name],
|
309
|
+
field[:value].inspect,
|
310
|
+
]
|
311
|
+
end
|
312
|
+
puts
|
303
313
|
end
|
304
|
-
puts
|
305
314
|
end
|
306
|
-
|
307
315
|
end
|