innodb_ruby 0.8.1 → 0.8.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/AUTHORS.md +12 -0
- data/LICENSE +29 -0
- data/bin/innodb_log +64 -9
- data/bin/innodb_space +95 -42
- data/lib/innodb/checksum.rb +1 -0
- data/lib/innodb/cursor.rb +8 -63
- data/lib/innodb/data_dictionary.rb +2 -0
- data/lib/innodb/data_type.rb +360 -0
- data/lib/innodb/field.rb +65 -10
- data/lib/innodb/fseg_entry.rb +2 -0
- data/lib/innodb/index.rb +5 -0
- data/lib/innodb/inode.rb +1 -0
- data/lib/innodb/list.rb +2 -0
- data/lib/innodb/log.rb +2 -0
- data/lib/innodb/log_block.rb +1 -0
- data/lib/innodb/log_group.rb +79 -0
- data/lib/innodb/page/fsp_hdr_xdes.rb +1 -0
- data/lib/innodb/page/index.rb +38 -42
- data/lib/innodb/page/index_compressed.rb +42 -0
- data/lib/innodb/page/inode.rb +1 -0
- data/lib/innodb/page/sys.rb +1 -0
- data/lib/innodb/page/sys_rseg_header.rb +1 -0
- data/lib/innodb/page/trx_sys.rb +1 -0
- data/lib/innodb/page.rb +1 -0
- data/lib/innodb/record.rb +49 -0
- data/lib/innodb/record_describer.rb +8 -0
- data/lib/innodb/space.rb +6 -0
- data/lib/innodb/system.rb +58 -0
- data/lib/innodb/undo_log.rb +1 -0
- data/lib/innodb/version.rb +2 -1
- data/lib/innodb/xdes.rb +1 -0
- data/lib/innodb.rb +4 -0
- metadata +13 -5
- data/lib/innodb/field_type.rb +0 -124
@@ -0,0 +1,360 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
require "bigdecimal"
|
5
|
+
require "date"
|
6
|
+
|
7
|
+
class Innodb::DataType
|
8
|
+
|
9
|
+
# MySQL's Bit-Value Type (BIT).
|
10
|
+
class BitType
|
11
|
+
attr_reader :name, :width
|
12
|
+
|
13
|
+
def initialize(base_type, modifiers, properties)
|
14
|
+
nbits = modifiers.fetch(0, 1)
|
15
|
+
raise "Unsupported width for BIT type." unless nbits >= 0 and nbits <= 64
|
16
|
+
@width = (nbits + 7) / 8
|
17
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
18
|
+
end
|
19
|
+
|
20
|
+
def value(data)
|
21
|
+
"0b%b" % BinData::const_get("Uint%dbe" % (@width * 8)).read(data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class IntegerType
|
26
|
+
attr_reader :name, :width
|
27
|
+
|
28
|
+
def initialize(base_type, modifiers, properties)
|
29
|
+
@width = base_type_width_map[base_type]
|
30
|
+
@unsigned = properties.include?(:UNSIGNED)
|
31
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
32
|
+
end
|
33
|
+
|
34
|
+
def base_type_width_map
|
35
|
+
{
|
36
|
+
:BOOL => 1,
|
37
|
+
:BOOLEAN => 1,
|
38
|
+
:TINYINT => 1,
|
39
|
+
:SMALLINT => 2,
|
40
|
+
:MEDIUMINT => 3,
|
41
|
+
:INT => 4,
|
42
|
+
:INT6 => 6,
|
43
|
+
:BIGINT => 8,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def value(data)
|
48
|
+
nbits = @width * 8
|
49
|
+
@unsigned ? get_uint(data, nbits) : get_int(data, nbits)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_uint(data, nbits)
|
53
|
+
BinData::const_get("Uint%dbe" % nbits).read(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_int(data, nbits)
|
57
|
+
BinData::const_get("Int%dbe" % nbits).read(data) ^ (-1 << (nbits - 1))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class FloatType
|
62
|
+
attr_reader :name, :width
|
63
|
+
|
64
|
+
def initialize(base_type, modifiers, properties)
|
65
|
+
@width = 4
|
66
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Read a little-endian single-precision floating-point number.
|
70
|
+
def value(data)
|
71
|
+
BinData::FloatLe.read(data)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class DoubleType
|
76
|
+
attr_reader :name, :width
|
77
|
+
|
78
|
+
def initialize(base_type, modifiers, properties)
|
79
|
+
@width = 8
|
80
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Read a little-endian double-precision floating-point number.
|
84
|
+
def value(data)
|
85
|
+
BinData::DoubleLe.read(data)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# MySQL's Fixed-Point Type (DECIMAL), stored in InnoDB as a binary string.
|
90
|
+
class DecimalType
|
91
|
+
attr_reader :name, :width
|
92
|
+
|
93
|
+
# The value is stored as a sequence of signed big-endian integers, each
|
94
|
+
# representing up to 9 digits of the integral and fractional parts. The
|
95
|
+
# first integer of the integral part and/or the last integer of the
|
96
|
+
# fractional part might be compressed (or packed) and are of variable
|
97
|
+
# length. The remaining integers (if any) are uncompressed and 32 bits
|
98
|
+
# wide.
|
99
|
+
MAX_DIGITS_PER_INTEGER = 9
|
100
|
+
BYTES_PER_DIGIT = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4]
|
101
|
+
|
102
|
+
def initialize(base_type, modifiers, properties)
|
103
|
+
precision, scale = sanity_check(modifiers)
|
104
|
+
integral = precision - scale
|
105
|
+
@uncomp_integral = integral / MAX_DIGITS_PER_INTEGER
|
106
|
+
@uncomp_fractional = scale / MAX_DIGITS_PER_INTEGER
|
107
|
+
@comp_integral = integral - (@uncomp_integral * MAX_DIGITS_PER_INTEGER)
|
108
|
+
@comp_fractional = scale - (@uncomp_fractional * MAX_DIGITS_PER_INTEGER)
|
109
|
+
@width = @uncomp_integral * 4 + BYTES_PER_DIGIT[@comp_integral] +
|
110
|
+
@comp_fractional * 4 + BYTES_PER_DIGIT[@comp_fractional]
|
111
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
112
|
+
end
|
113
|
+
|
114
|
+
def value(data)
|
115
|
+
# Strings representing the integral and fractional parts.
|
116
|
+
intg, frac = "", ""
|
117
|
+
|
118
|
+
stream = StringIO.new(data)
|
119
|
+
mask = sign_mask(stream)
|
120
|
+
|
121
|
+
intg << get_digits(stream, mask, @comp_integral)
|
122
|
+
|
123
|
+
(1 .. @uncomp_integral).each do
|
124
|
+
intg << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
|
125
|
+
end
|
126
|
+
|
127
|
+
(1 .. @uncomp_fractional).each do
|
128
|
+
frac << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
|
129
|
+
end
|
130
|
+
|
131
|
+
frac << get_digits(stream, mask, @comp_fractional)
|
132
|
+
|
133
|
+
# Convert to something resembling a string representation.
|
134
|
+
str = mask.to_s.chop + intg + '.' + frac
|
135
|
+
|
136
|
+
BigDecimal.new(str).to_s('F')
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
# Ensure width specification (if any) is compliant.
|
142
|
+
def sanity_check(modifiers)
|
143
|
+
raise "Invalid width specification" unless modifiers.size <= 2
|
144
|
+
precision = modifiers.fetch(0, 10)
|
145
|
+
raise "Unsupported precision for DECIMAL type" unless
|
146
|
+
precision >= 1 and precision <= 65
|
147
|
+
scale = modifiers.fetch(1, 0)
|
148
|
+
raise "Unsupported scale for DECIMAL type" unless
|
149
|
+
scale >= 0 and scale <= 30 and scale <= precision
|
150
|
+
[precision, scale]
|
151
|
+
end
|
152
|
+
|
153
|
+
# The sign is encoded in the high bit of the first byte/digit. The byte
|
154
|
+
# might be part of a larger integer, so apply the bit-flipper and push
|
155
|
+
# back the byte into the stream.
|
156
|
+
def sign_mask(stream)
|
157
|
+
byte = BinData::Uint8.read(stream)
|
158
|
+
sign = byte & 0x80
|
159
|
+
byte.assign(byte ^ 0x80)
|
160
|
+
stream.rewind
|
161
|
+
byte.write(stream)
|
162
|
+
stream.rewind
|
163
|
+
(sign == 0) ? -1 : 0
|
164
|
+
end
|
165
|
+
|
166
|
+
# Return a string representing an integer with a specific number of digits.
|
167
|
+
def get_digits(stream, mask, digits)
|
168
|
+
nbits = BYTES_PER_DIGIT[digits] * 8
|
169
|
+
return "" unless nbits > 0
|
170
|
+
value = (BinData::const_get("Int%dbe" % nbits).read(stream) ^ mask)
|
171
|
+
# Preserve leading zeros.
|
172
|
+
("%0" + digits.to_s + "d") % value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Fixed-length character type.
|
177
|
+
class CharacterType
|
178
|
+
attr_reader :name, :width
|
179
|
+
|
180
|
+
def initialize(base_type, modifiers, properties)
|
181
|
+
@width = modifiers.fetch(0, 1)
|
182
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class VariableCharacterType
|
187
|
+
attr_reader :name, :width
|
188
|
+
|
189
|
+
def initialize(base_type, modifiers, properties)
|
190
|
+
@width = modifiers[0]
|
191
|
+
raise "Invalid width specification" unless modifiers.size == 1
|
192
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
193
|
+
end
|
194
|
+
|
195
|
+
def value(data)
|
196
|
+
# The SQL standard defines that VARCHAR fields should have end-spaces
|
197
|
+
# stripped off.
|
198
|
+
data.sub(/[ ]+$/, "")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Fixed-length binary type.
|
203
|
+
class BinaryType
|
204
|
+
attr_reader :name, :width
|
205
|
+
|
206
|
+
def initialize(base_type, modifiers, properties)
|
207
|
+
@width = modifiers.fetch(0, 1)
|
208
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class VariableBinaryType
|
213
|
+
attr_reader :name, :width
|
214
|
+
|
215
|
+
def initialize(base_type, modifiers, properties)
|
216
|
+
@width = modifiers[0]
|
217
|
+
raise "Invalid width specification" unless modifiers.size == 1
|
218
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class BlobType
|
223
|
+
attr_reader :name
|
224
|
+
|
225
|
+
def initialize(base_type, modifiers, properties)
|
226
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class YearType
|
231
|
+
attr_reader :name, :width
|
232
|
+
|
233
|
+
def initialize(base_type, modifiers, properties)
|
234
|
+
@width = 1
|
235
|
+
@display_width = modifiers.fetch(0, 4)
|
236
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
237
|
+
end
|
238
|
+
|
239
|
+
def value(data)
|
240
|
+
year = BinData::Uint8.read(data)
|
241
|
+
return (year % 100).to_s if @display_width != 4
|
242
|
+
return (year + 1900).to_s if year != 0
|
243
|
+
"0000"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class TimeType
|
248
|
+
attr_reader :name, :width
|
249
|
+
|
250
|
+
def initialize(base_type, modifiers, properties)
|
251
|
+
@width = 3
|
252
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
253
|
+
end
|
254
|
+
|
255
|
+
def value(data)
|
256
|
+
time = BinData::Int24be.read(data) ^ (-1 << 23)
|
257
|
+
sign = "-" if time < 0
|
258
|
+
time = time.abs
|
259
|
+
"%s%02d:%02d:%02d" % [sign, time / 10000, (time / 100) % 100, time % 100]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class DateType
|
264
|
+
attr_reader :name, :width
|
265
|
+
|
266
|
+
def initialize(base_type, modifiers, properties)
|
267
|
+
@width = 3
|
268
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
269
|
+
end
|
270
|
+
|
271
|
+
def value(data)
|
272
|
+
date = BinData::Int24be.read(data) ^ (-1 << 23)
|
273
|
+
day = date & 0x1f
|
274
|
+
month = (date >> 5) & 0xf
|
275
|
+
year = date >> 9
|
276
|
+
"%04d-%02d-%02d" % [year, month, day]
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class DatetimeType
|
281
|
+
attr_reader :name, :width
|
282
|
+
|
283
|
+
def initialize(base_type, modifiers, properties)
|
284
|
+
@width = 8
|
285
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
286
|
+
end
|
287
|
+
|
288
|
+
def value(data)
|
289
|
+
datetime = BinData::Int64be.read(data) ^ (-1 << 63)
|
290
|
+
date = datetime / 1000000
|
291
|
+
year, month, day = [date / 10000, (date / 100) % 100, date % 100]
|
292
|
+
time = datetime - (date * 1000000)
|
293
|
+
hour, min, sec = [time / 10000, (time / 100) % 100, time % 100]
|
294
|
+
"%04d-%02d-%02d %02d:%02d:%02d" % [year, month, day, hour, min, sec]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class TimestampType
|
299
|
+
attr_reader :name, :width
|
300
|
+
|
301
|
+
def initialize(base_type, modifiers, properties)
|
302
|
+
@width = 4
|
303
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns the UTC timestamp as a value in 'YYYY-MM-DD HH:MM:SS' format.
|
307
|
+
def value(data)
|
308
|
+
timestamp = BinData::Uint32be.read(data)
|
309
|
+
return "0000-00-00 00:00:00" if timestamp.zero?
|
310
|
+
DateTime.strptime(timestamp.to_s, '%s').strftime "%Y-%m-%d %H:%M:%S"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Maps base type to data type class.
|
315
|
+
TYPES = {
|
316
|
+
:BIT => BitType,
|
317
|
+
:BOOL => IntegerType,
|
318
|
+
:BOOLEAN => IntegerType,
|
319
|
+
:TINYINT => IntegerType,
|
320
|
+
:SMALLINT => IntegerType,
|
321
|
+
:MEDIUMINT => IntegerType,
|
322
|
+
:INT => IntegerType,
|
323
|
+
:INT6 => IntegerType,
|
324
|
+
:BIGINT => IntegerType,
|
325
|
+
:FLOAT => FloatType,
|
326
|
+
:DOUBLE => DoubleType,
|
327
|
+
:DECIMAL => DecimalType,
|
328
|
+
:NUMERIC => DecimalType,
|
329
|
+
:CHAR => CharacterType,
|
330
|
+
:VARCHAR => VariableCharacterType,
|
331
|
+
:BINARY => BinaryType,
|
332
|
+
:VARBINARY => VariableBinaryType,
|
333
|
+
:TINYBLOB => BlobType,
|
334
|
+
:BLOB => BlobType,
|
335
|
+
:MEDIUMBLOB => BlobType,
|
336
|
+
:LONGBLOB => BlobType,
|
337
|
+
:TINYTEXT => BlobType,
|
338
|
+
:TEXT => BlobType,
|
339
|
+
:MEDIUMTEXT => BlobType,
|
340
|
+
:LONGTEXT => BlobType,
|
341
|
+
:YEAR => YearType,
|
342
|
+
:TIME => TimeType,
|
343
|
+
:DATE => DateType,
|
344
|
+
:DATETIME => DatetimeType,
|
345
|
+
:TIMESTAMP => TimestampType,
|
346
|
+
}
|
347
|
+
|
348
|
+
def self.make_name(base_type, modifiers, properties)
|
349
|
+
name = base_type.to_s
|
350
|
+
name << '(' + modifiers.join(',') + ')' if not modifiers.empty?
|
351
|
+
name << " "
|
352
|
+
name << properties.join(' ')
|
353
|
+
name.strip
|
354
|
+
end
|
355
|
+
|
356
|
+
def self.new(base_type, modifiers, properties)
|
357
|
+
raise "Data type '#{base_type}' is not supported" unless TYPES.key?(base_type)
|
358
|
+
TYPES[base_type].new(base_type, modifiers, properties)
|
359
|
+
end
|
360
|
+
end
|
data/lib/innodb/field.rb
CHANGED
@@ -1,23 +1,32 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
|
2
|
+
|
3
|
+
require "innodb/data_type"
|
3
4
|
|
4
5
|
# A single field in an InnoDB record (within an INDEX page). This class
|
5
6
|
# provides essential information to parse records, including the length
|
6
7
|
# of the fixed-width and variable-width portion of the field.
|
7
8
|
class Innodb::Field
|
8
|
-
attr_reader :position, :
|
9
|
+
attr_reader :position, :name, :data_type, :nullable
|
9
10
|
|
10
11
|
# Size of a reference to data stored externally to the page.
|
11
12
|
EXTERN_FIELD_SIZE = 20
|
12
13
|
|
13
|
-
def initialize(position,
|
14
|
+
def initialize(position, name, type_definition, *properties)
|
14
15
|
@position = position
|
15
|
-
@
|
16
|
+
@name = name
|
17
|
+
@nullable = properties.delete(:NOT_NULL) ? false : true
|
18
|
+
base_type, modifiers = parse_type_definition(type_definition.to_s)
|
19
|
+
@data_type = Innodb::DataType.new(base_type, modifiers, properties)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return whether this field can be NULL.
|
23
|
+
def nullable?
|
24
|
+
@nullable
|
16
25
|
end
|
17
26
|
|
18
27
|
# Return whether this field is NULL.
|
19
28
|
def null?(record)
|
20
|
-
|
29
|
+
nullable? && record[:header][:field_nulls][position]
|
21
30
|
end
|
22
31
|
|
23
32
|
# Return whether a part of this field is stored externally (off-page).
|
@@ -25,25 +34,71 @@ class Innodb::Field
|
|
25
34
|
record[:header][:field_externs][position]
|
26
35
|
end
|
27
36
|
|
37
|
+
def variable?
|
38
|
+
@data_type.is_a? Innodb::DataType::BlobType or
|
39
|
+
@data_type.is_a? Innodb::DataType::VariableBinaryType or
|
40
|
+
@data_type.is_a? Innodb::DataType::VariableCharacterType
|
41
|
+
end
|
42
|
+
|
43
|
+
def blob?
|
44
|
+
@data_type.is_a? Innodb::DataType::BlobType
|
45
|
+
end
|
46
|
+
|
28
47
|
# Return the actual length of this variable-length field.
|
29
48
|
def length(record)
|
30
|
-
if
|
49
|
+
if variable?
|
31
50
|
len = record[:header][:field_lengths][position]
|
32
51
|
else
|
33
|
-
len =
|
52
|
+
len = @data_type.width
|
34
53
|
end
|
35
54
|
extern?(record) ? len - EXTERN_FIELD_SIZE : len
|
36
55
|
end
|
37
56
|
|
38
57
|
# Read an InnoDB encoded data field.
|
39
58
|
def read(record, cursor)
|
59
|
+
cursor.name(@data_type.name) { cursor.get_bytes(length(record)) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Read the data value (e.g. encoded in the data).
|
63
|
+
def value(record, cursor)
|
40
64
|
return :NULL if null?(record)
|
41
|
-
|
65
|
+
data = read(record, cursor)
|
66
|
+
@data_type.respond_to?(:value) ? @data_type.value(data) : data
|
42
67
|
end
|
43
68
|
|
44
69
|
# Read an InnoDB external pointer field.
|
45
|
-
def
|
70
|
+
def extern(record, cursor)
|
46
71
|
return nil if not extern?(record)
|
47
|
-
cursor.name(
|
72
|
+
cursor.name(@name) { read_extern(cursor) }
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Return an external reference field. An extern field contains the page
|
78
|
+
# address and the length of the externally stored part of the record data.
|
79
|
+
def get_extern_reference(cursor)
|
80
|
+
{
|
81
|
+
:space_id => cursor.name("space_id") { cursor.get_uint32 },
|
82
|
+
:page_number => cursor.name("page_number") { cursor.get_uint32 },
|
83
|
+
:offset => cursor.name("offset") { cursor.get_uint32 },
|
84
|
+
:length => cursor.name("length") { cursor.get_uint64 & 0x3fffffff }
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def read_extern(cursor)
|
89
|
+
cursor.name("extern") { get_extern_reference(cursor) }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parse a data type definition and extract the base type and any modifiers.
|
93
|
+
def parse_type_definition(type_string)
|
94
|
+
if matches = /^([a-zA-Z0-9]+)(\(([0-9, ]+)\))?$/.match(type_string)
|
95
|
+
base_type = matches[1].upcase.to_sym
|
96
|
+
if matches[3]
|
97
|
+
modifiers = matches[3].sub(/[ ]/, "").split(/,/).map { |s| s.to_i }
|
98
|
+
else
|
99
|
+
modifiers = []
|
100
|
+
end
|
101
|
+
[base_type, modifiers]
|
102
|
+
end
|
48
103
|
end
|
49
104
|
end
|
data/lib/innodb/fseg_entry.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
|
2
3
|
# An InnoDB file segment entry, which appears in a few places, such as the
|
3
4
|
# FSEG header of INDEX pages, and in the TRX_SYS pages.
|
5
|
+
|
4
6
|
class Innodb::FsegEntry
|
5
7
|
# The size (in bytes) of an FSEG entry, which contains a two 32-bit integers
|
6
8
|
# and a 16-bit integer.
|
data/lib/innodb/index.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
|
2
3
|
# An InnoDB index B-tree, given an Innodb::Space and a root page number.
|
3
4
|
class Innodb::Index
|
4
5
|
attr_reader :root
|
@@ -97,6 +98,10 @@ class Innodb::Index
|
|
97
98
|
@root.fseg_header[name]
|
98
99
|
end
|
99
100
|
|
101
|
+
def field_names
|
102
|
+
record_describer.field_names
|
103
|
+
end
|
104
|
+
|
100
105
|
# Iterate through all file segments in the index.
|
101
106
|
def each_fseg
|
102
107
|
unless block_given?
|
data/lib/innodb/inode.rb
CHANGED
data/lib/innodb/list.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
|
2
3
|
# An abstract InnoDB "free list" or FLST (renamed to just "list" here as it
|
3
4
|
# frequently is used for structures that aren't free lists). This class must
|
4
5
|
# be sub-classed to provide an appropriate #object_from_address method.
|
6
|
+
|
5
7
|
class Innodb::List
|
6
8
|
# An "address", which consists of a page number and byte offset within the
|
7
9
|
# page. This points to the list "node" pointers (prev and next) of the
|
data/lib/innodb/log.rb
CHANGED
data/lib/innodb/log_block.rb
CHANGED
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
class Innodb::LogGroup
|
4
|
+
def initialize
|
5
|
+
@files = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_log(file)
|
9
|
+
if file = Innodb::Log.new(file)
|
10
|
+
@files.push file
|
11
|
+
else
|
12
|
+
raise "Couldn't open #{file}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def each_block
|
17
|
+
@files.each do |file|
|
18
|
+
file.each_block do |block_number, block|
|
19
|
+
yield block_number, block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def current_tail_position
|
25
|
+
max = 0
|
26
|
+
max_file = nil
|
27
|
+
max_block = nil
|
28
|
+
|
29
|
+
@files.each_with_index do |file, file_number|
|
30
|
+
file.each_block do |block_number, block|
|
31
|
+
if block.header[:block] > max
|
32
|
+
max = block.header[:block]
|
33
|
+
max_file = file_number
|
34
|
+
max_block = block_number
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
{ :file => max_file, :block => max_block }
|
40
|
+
end
|
41
|
+
|
42
|
+
def successor_position(position)
|
43
|
+
if position[:block] == @files[position[:file]].blocks
|
44
|
+
if position[:file] == @files.size
|
45
|
+
{ :file => 0, :block => 0 }
|
46
|
+
else
|
47
|
+
{ :file => position[:file] + 1, :block => 0 }
|
48
|
+
end
|
49
|
+
else
|
50
|
+
{ :file => position[:file], :block => position[:block] + 1 }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def block(file_number, block_number)
|
55
|
+
@files[file_number].block(block_number)
|
56
|
+
end
|
57
|
+
|
58
|
+
def block_if_newer(old_block, new_block)
|
59
|
+
return new_block if old_block.nil?
|
60
|
+
#puts "old: #{old_block.header[:block]} new: #{new_block.header[:block]}"
|
61
|
+
if new_block.header[:block] >= old_block.header[:block]
|
62
|
+
new_block
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def tail_blocks
|
67
|
+
position = current_tail_position
|
68
|
+
current_block = nil
|
69
|
+
while true
|
70
|
+
until block_if_newer(current_block, new_block = block(position[:file], position[:block]))
|
71
|
+
#puts "Waiting at the tail: #{position[:file]} #{position[:block]}"
|
72
|
+
sleep 0.1
|
73
|
+
end
|
74
|
+
yield new_block
|
75
|
+
position = successor_position(position)
|
76
|
+
current_block = new_block
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|