innodb_ruby 0.9.16 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +377 -757
- data/lib/innodb.rb +4 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -326
- 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 +154 -155
- data/lib/innodb/list.rb +191 -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.rb +417 -414
- 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 +964 -943
- 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/record.rb +121 -160
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +380 -418
- 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
- metadata +80 -11
data/lib/innodb/data_type.rb
CHANGED
@@ -1,416 +1,452 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "stringio"
|
4
4
|
require "bigdecimal"
|
5
5
|
require "date"
|
6
6
|
|
7
|
-
|
7
|
+
module Innodb
|
8
|
+
class DataType
|
9
|
+
# MySQL's Bit-Value Type (BIT).
|
10
|
+
class BitType
|
11
|
+
attr_reader :name
|
12
|
+
attr_reader :width
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
def initialize(base_type, modifiers, properties)
|
15
|
+
nbits = modifiers.fetch(0, 1)
|
16
|
+
raise "Unsupported width for BIT type." unless nbits >= 0 && nbits <= 64
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@width = (nbits + 7) / 8
|
17
|
-
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
18
|
-
end
|
18
|
+
@width = (nbits + 7) / 8
|
19
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
def value(data)
|
23
|
+
"0b%b" % BinData.const_get("Uint%dbe" % (@width * 8)).read(data)
|
24
|
+
end
|
22
25
|
end
|
23
|
-
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
class IntegerType
|
28
|
+
attr_reader :name
|
29
|
+
attr_reader :width
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
def initialize(base_type, modifiers, properties)
|
32
|
+
@width = base_type_width_map[base_type]
|
33
|
+
@unsigned = properties.include?(:UNSIGNED)
|
34
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
35
|
+
end
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
37
|
+
def base_type_width_map
|
38
|
+
{
|
39
|
+
BOOL: 1,
|
40
|
+
BOOLEAN: 1,
|
41
|
+
TINYINT: 1,
|
42
|
+
SMALLINT: 2,
|
43
|
+
MEDIUMINT: 3,
|
44
|
+
INT: 4,
|
45
|
+
INT6: 6,
|
46
|
+
BIGINT: 8,
|
47
|
+
}
|
48
|
+
end
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
def value(data)
|
51
|
+
nbits = @width * 8
|
52
|
+
@unsigned ? get_uint(data, nbits) : get_int(data, nbits)
|
53
|
+
end
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
def get_uint(data, nbits)
|
56
|
+
BinData.const_get("Uint%dbe" % nbits).read(data)
|
57
|
+
end
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
def get_int(data, nbits)
|
60
|
+
BinData.const_get("Int%dbe" % nbits).read(data) ^ (-1 << (nbits - 1))
|
61
|
+
end
|
58
62
|
end
|
59
|
-
end
|
60
63
|
|
61
|
-
|
62
|
-
|
64
|
+
class FloatType
|
65
|
+
attr_reader :name
|
66
|
+
attr_reader :width
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def initialize(base_type, modifiers, properties)
|
69
|
+
@width = 4
|
70
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
71
|
+
end
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
# Read a little-endian single-precision floating-point number.
|
74
|
+
def value(data)
|
75
|
+
BinData::FloatLe.read(data)
|
76
|
+
end
|
72
77
|
end
|
73
|
-
end
|
74
78
|
|
75
|
-
|
76
|
-
|
79
|
+
class DoubleType
|
80
|
+
attr_reader :name
|
81
|
+
attr_reader :width
|
77
82
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
83
|
+
def initialize(base_type, modifiers, properties)
|
84
|
+
@width = 8
|
85
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
86
|
+
end
|
82
87
|
|
83
|
-
|
84
|
-
|
85
|
-
|
88
|
+
# Read a little-endian double-precision floating-point number.
|
89
|
+
def value(data)
|
90
|
+
BinData::DoubleLe.read(data)
|
91
|
+
end
|
86
92
|
end
|
87
|
-
end
|
88
93
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
94
|
+
# MySQL's Fixed-Point Type (DECIMAL), stored in InnoDB as a binary string.
|
95
|
+
class DecimalType
|
96
|
+
attr_reader :name
|
97
|
+
attr_reader :width
|
98
|
+
|
99
|
+
# The value is stored as a sequence of signed big-endian integers, each
|
100
|
+
# representing up to 9 digits of the integral and fractional parts. The
|
101
|
+
# first integer of the integral part and/or the last integer of the
|
102
|
+
# fractional part might be compressed (or packed) and are of variable
|
103
|
+
# length. The remaining integers (if any) are uncompressed and 32 bits
|
104
|
+
# wide.
|
105
|
+
MAX_DIGITS_PER_INTEGER = 9
|
106
|
+
BYTES_PER_DIGIT = [0, 1, 1, 2, 2, 3, 3, 4, 4, 4].freeze
|
107
|
+
|
108
|
+
def initialize(base_type, modifiers, properties)
|
109
|
+
precision, scale = sanity_check(modifiers)
|
110
|
+
integral = precision - scale
|
111
|
+
@uncomp_integral = integral / MAX_DIGITS_PER_INTEGER
|
112
|
+
@uncomp_fractional = scale / MAX_DIGITS_PER_INTEGER
|
113
|
+
@comp_integral = integral - (@uncomp_integral * MAX_DIGITS_PER_INTEGER)
|
114
|
+
@comp_fractional = scale - (@uncomp_fractional * MAX_DIGITS_PER_INTEGER)
|
115
|
+
@width = @uncomp_integral * 4 + BYTES_PER_DIGIT[@comp_integral] +
|
116
|
+
@comp_fractional * 4 + BYTES_PER_DIGIT[@comp_fractional]
|
117
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
118
|
+
end
|
113
119
|
|
114
|
-
|
115
|
-
|
116
|
-
|
120
|
+
def value(data)
|
121
|
+
# Strings representing the integral and fractional parts.
|
122
|
+
intg = "".dup
|
123
|
+
frac = "".dup
|
117
124
|
|
118
|
-
|
119
|
-
|
125
|
+
stream = StringIO.new(data)
|
126
|
+
mask = sign_mask(stream)
|
120
127
|
|
121
|
-
|
128
|
+
intg << get_digits(stream, mask, @comp_integral)
|
122
129
|
|
123
|
-
|
124
|
-
|
125
|
-
|
130
|
+
(1..@uncomp_integral).each do
|
131
|
+
intg << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
|
132
|
+
end
|
133
|
+
|
134
|
+
(1..@uncomp_fractional).each do
|
135
|
+
frac << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
|
136
|
+
end
|
137
|
+
|
138
|
+
frac << get_digits(stream, mask, @comp_fractional)
|
139
|
+
frac = "0" if frac.empty?
|
140
|
+
|
141
|
+
# Convert to something resembling a string representation.
|
142
|
+
str = "#{mask.to_s.chop}#{intg}.#{frac}"
|
126
143
|
|
127
|
-
|
128
|
-
frac << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
|
144
|
+
BigDecimal(str).to_s("F")
|
129
145
|
end
|
130
146
|
|
131
|
-
|
132
|
-
frac = "0" if frac.empty?
|
147
|
+
private
|
133
148
|
|
134
|
-
#
|
135
|
-
|
149
|
+
# Ensure width specification (if any) is compliant.
|
150
|
+
def sanity_check(modifiers)
|
151
|
+
raise "Invalid width specification" unless modifiers.size <= 2
|
136
152
|
|
137
|
-
|
138
|
-
|
153
|
+
precision = modifiers.fetch(0, 10)
|
154
|
+
raise "Unsupported precision for DECIMAL type" unless precision >= 1 && precision <= 65
|
139
155
|
|
140
|
-
|
141
|
-
|
142
|
-
# Ensure width specification (if any) is compliant.
|
143
|
-
def sanity_check(modifiers)
|
144
|
-
raise "Invalid width specification" unless modifiers.size <= 2
|
145
|
-
precision = modifiers.fetch(0, 10)
|
146
|
-
raise "Unsupported precision for DECIMAL type" unless
|
147
|
-
precision >= 1 and precision <= 65
|
148
|
-
scale = modifiers.fetch(1, 0)
|
149
|
-
raise "Unsupported scale for DECIMAL type" unless
|
150
|
-
scale >= 0 and scale <= 30 and scale <= precision
|
151
|
-
[precision, scale]
|
152
|
-
end
|
156
|
+
scale = modifiers.fetch(1, 0)
|
157
|
+
raise "Unsupported scale for DECIMAL type" unless scale >= 0 && scale <= 30 && scale <= precision
|
153
158
|
|
154
|
-
|
155
|
-
|
156
|
-
# back the byte into the stream.
|
157
|
-
def sign_mask(stream)
|
158
|
-
byte = BinData::Uint8.read(stream)
|
159
|
-
sign = byte & 0x80
|
160
|
-
byte.assign(byte ^ 0x80)
|
161
|
-
stream.rewind
|
162
|
-
byte.write(stream)
|
163
|
-
stream.rewind
|
164
|
-
(sign == 0) ? -1 : 0
|
165
|
-
end
|
159
|
+
[precision, scale]
|
160
|
+
end
|
166
161
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
162
|
+
# The sign is encoded in the high bit of the first byte/digit. The byte
|
163
|
+
# might be part of a larger integer, so apply the bit-flipper and push
|
164
|
+
# back the byte into the stream.
|
165
|
+
def sign_mask(stream)
|
166
|
+
byte = BinData::Uint8.read(stream)
|
167
|
+
sign = byte & 0x80
|
168
|
+
byte.assign(byte ^ 0x80)
|
169
|
+
stream.rewind
|
170
|
+
byte.write(stream)
|
171
|
+
stream.rewind
|
172
|
+
sign.zero? ? -1 : 0
|
173
|
+
end
|
176
174
|
|
177
|
-
|
178
|
-
|
179
|
-
|
175
|
+
# Return a string representing an integer with a specific number of digits.
|
176
|
+
def get_digits(stream, mask, digits)
|
177
|
+
nbits = BYTES_PER_DIGIT[digits] * 8
|
178
|
+
return "" unless nbits.positive?
|
180
179
|
|
181
|
-
|
182
|
-
|
183
|
-
|
180
|
+
value = (BinData.const_get("Int%dbe" % nbits).read(stream) ^ mask)
|
181
|
+
# Preserve leading zeros.
|
182
|
+
"%0#{digits}d" % value
|
183
|
+
end
|
184
184
|
end
|
185
185
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
end
|
191
|
-
end
|
186
|
+
# Fixed-length character type.
|
187
|
+
class CharacterType
|
188
|
+
attr_reader :name
|
189
|
+
attr_reader :width
|
192
190
|
|
193
|
-
|
194
|
-
|
191
|
+
def initialize(base_type, modifiers, properties)
|
192
|
+
@width = modifiers.fetch(0, 1)
|
193
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
194
|
+
end
|
195
195
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
196
|
+
def value(data)
|
197
|
+
# The SQL standard defines that CHAR fields should have end-spaces
|
198
|
+
# stripped off.
|
199
|
+
data.sub(/ +$/, "")
|
200
|
+
end
|
200
201
|
end
|
201
202
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
data.sub(/[ ]+$/, "")
|
206
|
-
end
|
207
|
-
end
|
203
|
+
class VariableCharacterType
|
204
|
+
attr_reader :name
|
205
|
+
attr_reader :width
|
208
206
|
|
209
|
-
|
210
|
-
|
211
|
-
|
207
|
+
def initialize(base_type, modifiers, properties)
|
208
|
+
@width = modifiers[0]
|
209
|
+
raise "Invalid width specification" unless modifiers.size == 1
|
210
|
+
|
211
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
212
|
+
end
|
212
213
|
|
213
|
-
|
214
|
-
|
215
|
-
|
214
|
+
def value(data)
|
215
|
+
# The SQL standard defines that VARCHAR fields should have end-spaces
|
216
|
+
# stripped off.
|
217
|
+
data.sub(/ +$/, "")
|
218
|
+
end
|
216
219
|
end
|
217
|
-
end
|
218
220
|
|
219
|
-
|
220
|
-
|
221
|
+
# Fixed-length binary type.
|
222
|
+
class BinaryType
|
223
|
+
attr_reader :name
|
224
|
+
attr_reader :width
|
221
225
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
+
def initialize(base_type, modifiers, properties)
|
227
|
+
@width = modifiers.fetch(0, 1)
|
228
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
229
|
+
end
|
226
230
|
end
|
227
|
-
end
|
228
231
|
|
229
|
-
|
230
|
-
|
232
|
+
class VariableBinaryType
|
233
|
+
attr_reader :name
|
234
|
+
attr_reader :width
|
235
|
+
|
236
|
+
def initialize(base_type, modifiers, properties)
|
237
|
+
@width = modifiers[0]
|
238
|
+
raise "Invalid width specification" unless modifiers.size == 1
|
231
239
|
|
232
|
-
|
233
|
-
|
240
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
241
|
+
end
|
234
242
|
end
|
235
|
-
end
|
236
243
|
|
237
|
-
|
238
|
-
|
244
|
+
class BlobType
|
245
|
+
attr_reader :name
|
239
246
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
247
|
+
def initialize(base_type, modifiers, properties)
|
248
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
249
|
+
end
|
244
250
|
end
|
245
251
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
return (year + 1900).to_s if year != 0
|
250
|
-
"0000"
|
251
|
-
end
|
252
|
-
end
|
252
|
+
class YearType
|
253
|
+
attr_reader :name
|
254
|
+
attr_reader :width
|
253
255
|
|
254
|
-
|
255
|
-
|
256
|
+
def initialize(base_type, modifiers, properties)
|
257
|
+
@width = 1
|
258
|
+
@display_width = modifiers.fetch(0, 4)
|
259
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
260
|
+
end
|
256
261
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
262
|
+
def value(data)
|
263
|
+
year = BinData::Uint8.read(data)
|
264
|
+
return (year % 100).to_s if @display_width != 4
|
265
|
+
return (year + 1900).to_s if year != 0
|
261
266
|
|
262
|
-
|
263
|
-
|
264
|
-
sign = "-" if time < 0
|
265
|
-
time = time.abs
|
266
|
-
"%s%02d:%02d:%02d" % [sign, time / 10000, (time / 100) % 100, time % 100]
|
267
|
+
"0000"
|
268
|
+
end
|
267
269
|
end
|
268
|
-
end
|
269
270
|
|
270
|
-
|
271
|
-
|
271
|
+
class TimeType
|
272
|
+
attr_reader :name
|
273
|
+
attr_reader :width
|
272
274
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
275
|
+
def initialize(base_type, modifiers, properties)
|
276
|
+
@width = 3
|
277
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
278
|
+
end
|
277
279
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
280
|
+
def value(data)
|
281
|
+
time = BinData::Int24be.read(data) ^ (-1 << 23)
|
282
|
+
sign = "-" if time.negative?
|
283
|
+
time = time.abs
|
284
|
+
"%s%02d:%02d:%02d" % [sign, time / 10_000, (time / 100) % 100, time % 100]
|
285
|
+
end
|
284
286
|
end
|
285
|
-
end
|
286
287
|
|
287
|
-
|
288
|
-
|
288
|
+
class DateType
|
289
|
+
attr_reader :name
|
290
|
+
attr_reader :width
|
289
291
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
292
|
+
def initialize(base_type, modifiers, properties)
|
293
|
+
@width = 3
|
294
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
295
|
+
end
|
294
296
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
297
|
+
def value(data)
|
298
|
+
date = BinData::Int24be.read(data) ^ (-1 << 23)
|
299
|
+
day = date & 0x1f
|
300
|
+
month = (date >> 5) & 0xf
|
301
|
+
year = date >> 9
|
302
|
+
"%04d-%02d-%02d" % [year, month, day]
|
303
|
+
end
|
302
304
|
end
|
303
|
-
end
|
304
305
|
|
305
|
-
|
306
|
-
|
306
|
+
class DatetimeType
|
307
|
+
attr_reader :name
|
308
|
+
attr_reader :width
|
307
309
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
310
|
+
def initialize(base_type, modifiers, properties)
|
311
|
+
@width = 8
|
312
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
313
|
+
end
|
312
314
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
315
|
+
def value(data)
|
316
|
+
datetime = BinData::Int64be.read(data) ^ (-1 << 63)
|
317
|
+
date = datetime / 1_000_000
|
318
|
+
year = date / 10_000
|
319
|
+
month = (date / 100) % 100
|
320
|
+
day = date % 100
|
321
|
+
time = datetime - (date * 1_000_000)
|
322
|
+
hour = time / 10_000
|
323
|
+
min = (time / 100) % 100
|
324
|
+
sec = time % 100
|
325
|
+
"%04d-%02d-%02d %02d:%02d:%02d" % [year, month, day, hour, min, sec]
|
326
|
+
end
|
318
327
|
end
|
319
|
-
end
|
320
328
|
|
321
|
-
|
322
|
-
|
323
|
-
|
329
|
+
class TimestampType
|
330
|
+
attr_reader :name
|
331
|
+
attr_reader :width
|
324
332
|
|
325
|
-
|
326
|
-
|
327
|
-
|
333
|
+
def initialize(base_type, modifiers, properties)
|
334
|
+
@width = 4
|
335
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
336
|
+
end
|
328
337
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
338
|
+
# Returns the UTC timestamp as a value in 'YYYY-MM-DD HH:MM:SS' format.
|
339
|
+
def value(data)
|
340
|
+
timestamp = BinData::Uint32be.read(data)
|
341
|
+
return "0000-00-00 00:00:00" if timestamp.zero?
|
333
342
|
|
334
|
-
|
335
|
-
|
343
|
+
DateTime.strptime(timestamp.to_s, "%s").strftime "%Y-%m-%d %H:%M:%S"
|
344
|
+
end
|
336
345
|
end
|
337
|
-
end
|
338
346
|
|
339
|
-
|
340
|
-
|
341
|
-
|
347
|
+
#
|
348
|
+
# Data types for InnoDB system columns.
|
349
|
+
#
|
342
350
|
|
343
|
-
|
351
|
+
# Transaction ID.
|
352
|
+
class TransactionIdType
|
353
|
+
attr_reader :name
|
354
|
+
attr_reader :width
|
344
355
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
356
|
+
def initialize(base_type, modifiers, properties)
|
357
|
+
@width = 6
|
358
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
359
|
+
end
|
349
360
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
:rseg_id => read_bits_at_offset(roll_ptr, 7, 48),
|
354
|
-
:undo_log => {
|
355
|
-
:page => read_bits_at_offset(roll_ptr, 32, 16),
|
356
|
-
:offset => read_bits_at_offset(roll_ptr, 16, 0),
|
357
|
-
}
|
358
|
-
}
|
361
|
+
def read(cursor)
|
362
|
+
cursor.name("transaction_id") { cursor.read_uint48 }
|
363
|
+
end
|
359
364
|
end
|
360
365
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
end
|
366
|
+
# Rollback data pointer.
|
367
|
+
class RollPointerType
|
368
|
+
extend ReadBitsAtOffset
|
365
369
|
|
366
|
-
|
370
|
+
Pointer = Struct.new(
|
371
|
+
:is_insert,
|
372
|
+
:rseg_id,
|
373
|
+
:undo_log,
|
374
|
+
keyword_init: true
|
375
|
+
)
|
367
376
|
|
368
|
-
|
369
|
-
|
370
|
-
:BIT => BitType,
|
371
|
-
:BOOL => IntegerType,
|
372
|
-
:BOOLEAN => IntegerType,
|
373
|
-
:TINYINT => IntegerType,
|
374
|
-
:SMALLINT => IntegerType,
|
375
|
-
:MEDIUMINT => IntegerType,
|
376
|
-
:INT => IntegerType,
|
377
|
-
:INT6 => IntegerType,
|
378
|
-
:BIGINT => IntegerType,
|
379
|
-
:FLOAT => FloatType,
|
380
|
-
:DOUBLE => DoubleType,
|
381
|
-
:DECIMAL => DecimalType,
|
382
|
-
:NUMERIC => DecimalType,
|
383
|
-
:CHAR => CharacterType,
|
384
|
-
:VARCHAR => VariableCharacterType,
|
385
|
-
:BINARY => BinaryType,
|
386
|
-
:VARBINARY => VariableBinaryType,
|
387
|
-
:TINYBLOB => BlobType,
|
388
|
-
:BLOB => BlobType,
|
389
|
-
:MEDIUMBLOB => BlobType,
|
390
|
-
:LONGBLOB => BlobType,
|
391
|
-
:TINYTEXT => BlobType,
|
392
|
-
:TEXT => BlobType,
|
393
|
-
:MEDIUMTEXT => BlobType,
|
394
|
-
:LONGTEXT => BlobType,
|
395
|
-
:YEAR => YearType,
|
396
|
-
:TIME => TimeType,
|
397
|
-
:DATE => DateType,
|
398
|
-
:DATETIME => DatetimeType,
|
399
|
-
:TIMESTAMP => TimestampType,
|
400
|
-
:TRX_ID => TransactionIdType,
|
401
|
-
:ROLL_PTR => RollPointerType,
|
402
|
-
}
|
403
|
-
|
404
|
-
def self.make_name(base_type, modifiers, properties)
|
405
|
-
name = base_type.to_s
|
406
|
-
name << '(' + modifiers.join(',') + ')' if not modifiers.empty?
|
407
|
-
name << " "
|
408
|
-
name << properties.join(' ')
|
409
|
-
name.strip
|
410
|
-
end
|
377
|
+
attr_reader :name
|
378
|
+
attr_reader :width
|
411
379
|
|
412
|
-
|
413
|
-
|
414
|
-
|
380
|
+
def initialize(base_type, modifiers, properties)
|
381
|
+
@width = 7
|
382
|
+
@name = Innodb::DataType.make_name(base_type, modifiers, properties)
|
383
|
+
end
|
384
|
+
|
385
|
+
def self.parse_roll_pointer(roll_ptr)
|
386
|
+
Pointer.new(
|
387
|
+
is_insert: read_bits_at_offset(roll_ptr, 1, 55) == 1,
|
388
|
+
rseg_id: read_bits_at_offset(roll_ptr, 7, 48),
|
389
|
+
undo_log: Innodb::Page::Address.new(
|
390
|
+
page: read_bits_at_offset(roll_ptr, 32, 16),
|
391
|
+
offset: read_bits_at_offset(roll_ptr, 16, 0)
|
392
|
+
)
|
393
|
+
)
|
394
|
+
end
|
395
|
+
|
396
|
+
def value(data)
|
397
|
+
roll_ptr = BinData::Uint56be.read(data)
|
398
|
+
self.class.parse_roll_pointer(roll_ptr)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
# Maps base type to data type class.
|
403
|
+
TYPES = {
|
404
|
+
BIT: BitType,
|
405
|
+
BOOL: IntegerType,
|
406
|
+
BOOLEAN: IntegerType,
|
407
|
+
TINYINT: IntegerType,
|
408
|
+
SMALLINT: IntegerType,
|
409
|
+
MEDIUMINT: IntegerType,
|
410
|
+
INT: IntegerType,
|
411
|
+
INT6: IntegerType,
|
412
|
+
BIGINT: IntegerType,
|
413
|
+
FLOAT: FloatType,
|
414
|
+
DOUBLE: DoubleType,
|
415
|
+
DECIMAL: DecimalType,
|
416
|
+
NUMERIC: DecimalType,
|
417
|
+
CHAR: CharacterType,
|
418
|
+
VARCHAR: VariableCharacterType,
|
419
|
+
BINARY: BinaryType,
|
420
|
+
VARBINARY: VariableBinaryType,
|
421
|
+
TINYBLOB: BlobType,
|
422
|
+
BLOB: BlobType,
|
423
|
+
MEDIUMBLOB: BlobType,
|
424
|
+
LONGBLOB: BlobType,
|
425
|
+
TINYTEXT: BlobType,
|
426
|
+
TEXT: BlobType,
|
427
|
+
MEDIUMTEXT: BlobType,
|
428
|
+
LONGTEXT: BlobType,
|
429
|
+
YEAR: YearType,
|
430
|
+
TIME: TimeType,
|
431
|
+
DATE: DateType,
|
432
|
+
DATETIME: DatetimeType,
|
433
|
+
TIMESTAMP: TimestampType,
|
434
|
+
TRX_ID: TransactionIdType,
|
435
|
+
ROLL_PTR: RollPointerType,
|
436
|
+
}.freeze
|
437
|
+
|
438
|
+
def self.make_name(base_type, modifiers, properties)
|
439
|
+
name = base_type.to_s.dup
|
440
|
+
name << "(#{modifiers.join(',')})" unless modifiers.empty?
|
441
|
+
name << " "
|
442
|
+
name << properties.join(" ")
|
443
|
+
name.strip
|
444
|
+
end
|
445
|
+
|
446
|
+
def self.new(base_type, modifiers, properties)
|
447
|
+
raise "Data type '#{base_type}' is not supported" unless TYPES.key?(base_type)
|
448
|
+
|
449
|
+
TYPES[base_type].new(base_type, modifiers, properties)
|
450
|
+
end
|
415
451
|
end
|
416
452
|
end
|