Class: Innodb::UndoRecord
- Inherits:
-
Object
- Object
- Innodb::UndoRecord
show all
- Extended by:
- Forwardable
- Defined in:
- lib/innodb/undo_record.rb
Defined Under Namespace
Classes: Field, Header, HeaderInfo, Record
Constant Summary
collapse
- TYPE =
Possible undo record types.
{
11 => :insert,
12 => :update_existing,
13 => :update_deleted,
14 => :delete,
}.freeze
- TYPES_WITH_PREVIOUS_VERSIONS =
%i[
update_existing
update_deleted
delete
].freeze
- TYPE_MASK =
0x0f
- COMPILATION_INFO_MASK =
0x70
- COMPILATION_INFO_SHIFT =
4
- COMPILATION_INFO_NO_ORDER_CHANGE_BV =
1
- COMPILATION_INFO_NO_SIZE_CHANGE_BV =
2
- EXTERN_FLAG =
0x80
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(undo_page, position) ⇒ UndoRecord
Returns a new instance of UndoRecord.
53
54
55
56
57
58
59
|
# File 'lib/innodb/undo_record.rb', line 53
def initialize(undo_page, position)
@undo_page = undo_page
@position = position
@undo_log = nil
@index_page = nil
end
|
Instance Attribute Details
#index_page ⇒ Object
Returns the value of attribute index_page.
51
52
53
|
# File 'lib/innodb/undo_record.rb', line 51
def index_page
@index_page
end
|
#position ⇒ Object
Returns the value of attribute position.
48
49
50
|
# File 'lib/innodb/undo_record.rb', line 48
def position
@position
end
|
#undo_log ⇒ Object
Returns the value of attribute undo_log.
50
51
52
|
# File 'lib/innodb/undo_record.rb', line 50
def undo_log
@undo_log
end
|
#undo_page ⇒ Object
Returns the value of attribute undo_page.
47
48
49
|
# File 'lib/innodb/undo_record.rb', line 47
def undo_page
@undo_page
end
|
Instance Method Details
#cursor(position) ⇒ Object
Return a BufferCursor starting before the header.
85
86
87
88
89
90
|
# File 'lib/innodb/undo_record.rb', line 85
def cursor(position)
new_cursor = @undo_page.cursor(position)
new_cursor.push_name("undo_log[#{@undo_log.position}]") if @undo_log
new_cursor.push_name("undo_record[#{@position}]")
new_cursor
end
|
#dump ⇒ Object
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
# File 'lib/innodb/undo_record.rb', line 274
def dump
puts "Undo record at offset %i" % offset
puts
puts "Header:"
puts " %-25s: %i" % ["Previous record offset", [:prev]]
puts " %-25s: %i" % ["Next record offset", [:next]]
puts " %-25s: %s" % ["Type", [:type]]
puts
puts "System fields:"
puts " Transaction ID: %s" % trx_id
puts " Roll Pointer:"
puts " Undo Log: page %i, offset %i" % [
roll_ptr[:undo_log][:page],
roll_ptr[:undo_log][:offset],
]
puts " Rollback Segment ID: %i" % roll_ptr[:rseg_id]
puts
puts "Key fields:"
key.each do |field|
puts " %s: %s" % [
field[:name],
field[:value].inspect,
]
end
puts
puts "Non-key fields:"
row.each do |field|
next unless field
puts " %s: %s" % [
field[:name],
field[:value].inspect,
]
end
puts
end
|
#get(prev_or_next) ⇒ Object
139
140
141
142
143
144
|
# File 'lib/innodb/undo_record.rb', line 139
def get(prev_or_next)
return if [prev_or_next].zero?
new_undo_record = new_subordinate(@undo_page, [prev_or_next])
new_undo_record if new_undo_record.type
end
|
#key_string ⇒ Object
232
233
234
|
# File 'lib/innodb/undo_record.rb', line 232
def key_string
key&.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }&.join(", ")
end
|
#new_subordinate(undo_page, position) ⇒ Object
61
62
63
64
65
66
67
|
# File 'lib/innodb/undo_record.rb', line 61
def new_subordinate(undo_page, position)
new_undo_record = self.class.new(undo_page, position)
new_undo_record.undo_log = undo_log
new_undo_record.index_page = index_page
new_undo_record
end
|
#next ⇒ Object
150
151
152
|
# File 'lib/innodb/undo_record.rb', line 150
def next
get(:next)
end
|
The header really starts 2 bytes before the undo record position, as the pointer to the previous record is written there.
71
72
73
|
# File 'lib/innodb/undo_record.rb', line 71
def
@position - 2
end
|
#pos_record ⇒ Object
80
81
82
|
# File 'lib/innodb/undo_record.rb', line 80
def pos_record
+
end
|
#prev ⇒ Object
146
147
148
|
# File 'lib/innodb/undo_record.rb', line 146
def prev
get(:prev)
end
|
#prev_by_history ⇒ Object
Find the previous row version by following the roll_ptr from one undo record to the next (backwards through the record version history). Since we are operating without the benefit of knowing about active transactions and without protection from purge, check that everything looks sane before returning it.
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
|
# File 'lib/innodb/undo_record.rb', line 253
def prev_by_history
return unless previous_version?
undo_log = roll_ptr[:undo_log]
older_undo_page = @undo_page.space.page(undo_log[:page])
return unless older_undo_page.is_a?(Innodb::Page::UndoLog)
older_undo_record = new_subordinate(older_undo_page, undo_log[:offset])
return unless older_undo_record && table_id == older_undo_record.table_id
return unless older_undo_record.trx_id.nil? || trx_id >= older_undo_record.trx_id
older_undo_record
end
|
#previous_version? ⇒ Boolean
#read_record ⇒ Object
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
# File 'lib/innodb/undo_record.rb', line 158
def read_record
cursor(pos_record).name("record") do |c|
this_record = Record.new(
page: undo_page.offset,
offset: position,
header: ,
undo_no: c.name("undo_no") { c.read_imc_uint64 },
table_id: c.name("table_id") { c.read_imc_uint64 }
)
if previous_version?
this_record.info_bits = c.name("info_bits") { c.read_uint8 }
this_record.trx_id = c.name("trx_id") { c.read_ic_uint64 }
this_record.roll_ptr = c.name("roll_ptr") do
Innodb::DataType::RollPointerType.parse_roll_pointer(c.read_ic_uint64)
end
end
if index_page
read_record_fields(this_record, c)
else
this_record.data = c.read_bytes([:next] - c.position - 2)
end
this_record
end
end
|
#read_record_fields(this_record, cursor) ⇒ Object
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
# File 'lib/innodb/undo_record.rb', line 187
def read_record_fields(this_record, cursor)
this_record.key = []
index_page.record_format[:key].each do |field|
length = cursor.name("field_length") { cursor.read_ic_uint32 }
value = cursor.name(field.name) { field.value_by_length(cursor, length) }
this_record.key[field.position] = Field.new(name: field.name, type: field.data_type.name, value: value)
end
return unless previous_version?
field_count = cursor.name("field_count") { cursor.read_ic_uint32 }
this_record.row = Array.new(index_page.record_format[:row].size)
field_count.times do
field_number = cursor.name("field_number[#{field_count}]") { cursor.read_ic_uint32 }
field = nil
field_index = nil
index_page.record_format[:row].each_with_index do |candidate_field, index|
if candidate_field.position == field_number
field = candidate_field
field_index = index
end
end
raise "Unknown field #{field_number}" unless field
length = cursor.name("field_length") { cursor.read_ic_uint32 }
value = cursor.name(field.name) { field.value_by_length(cursor, length) }
this_record.row[field_index] = Field.new(name: field.name, type: field.data_type.name, value: value)
end
end
|
#record_size ⇒ Object
154
155
156
|
# File 'lib/innodb/undo_record.rb', line 154
def record_size
[:next] - @position -
end
|
#row ⇒ Object
236
237
238
|
# File 'lib/innodb/undo_record.rb', line 236
def row
undo_record[:row]
end
|
#row_string ⇒ Object
240
241
242
|
# File 'lib/innodb/undo_record.rb', line 240
def row_string
row&.compact&.map { |r| r && format("%s=%s", r[:name], r[:value].inspect) }&.join(", ")
end
|
76
77
78
|
# File 'lib/innodb/undo_record.rb', line 76
def
2 + 2 + 1
end
|
#string ⇒ Object
244
245
246
|
# File 'lib/innodb/undo_record.rb', line 244
def string
"(%s) → (%s)" % [key_string, row_string]
end
|
#undo_record ⇒ Object
220
221
222
|
# File 'lib/innodb/undo_record.rb', line 220
def undo_record
@undo_record ||= read_record
end
|