innodb_ruby 0.8.1 → 0.8.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|