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.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +377 -757
  5. data/lib/innodb.rb +4 -5
  6. data/lib/innodb/checksum.rb +26 -24
  7. data/lib/innodb/data_dictionary.rb +490 -550
  8. data/lib/innodb/data_type.rb +362 -326
  9. data/lib/innodb/field.rb +102 -89
  10. data/lib/innodb/fseg_entry.rb +22 -26
  11. data/lib/innodb/history.rb +21 -21
  12. data/lib/innodb/history_list.rb +72 -76
  13. data/lib/innodb/ibuf_bitmap.rb +36 -36
  14. data/lib/innodb/ibuf_index.rb +6 -2
  15. data/lib/innodb/index.rb +245 -276
  16. data/lib/innodb/inode.rb +154 -155
  17. data/lib/innodb/list.rb +191 -183
  18. data/lib/innodb/log.rb +139 -110
  19. data/lib/innodb/log_block.rb +100 -91
  20. data/lib/innodb/log_group.rb +53 -64
  21. data/lib/innodb/log_reader.rb +97 -96
  22. data/lib/innodb/log_record.rb +328 -279
  23. data/lib/innodb/lsn.rb +86 -81
  24. data/lib/innodb/page.rb +417 -414
  25. data/lib/innodb/page/blob.rb +82 -83
  26. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  27. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  28. data/lib/innodb/page/index.rb +964 -943
  29. data/lib/innodb/page/index_compressed.rb +34 -34
  30. data/lib/innodb/page/inode.rb +103 -112
  31. data/lib/innodb/page/sys.rb +13 -15
  32. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  33. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  34. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  35. data/lib/innodb/page/trx_sys.rb +204 -182
  36. data/lib/innodb/page/undo_log.rb +106 -92
  37. data/lib/innodb/record.rb +121 -160
  38. data/lib/innodb/record_describer.rb +66 -68
  39. data/lib/innodb/space.rb +380 -418
  40. data/lib/innodb/stats.rb +33 -35
  41. data/lib/innodb/system.rb +149 -171
  42. data/lib/innodb/undo_log.rb +129 -107
  43. data/lib/innodb/undo_record.rb +255 -247
  44. data/lib/innodb/util/buffer_cursor.rb +81 -79
  45. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  46. data/lib/innodb/version.rb +2 -2
  47. data/lib/innodb/xdes.rb +144 -142
  48. metadata +80 -11
@@ -1,416 +1,452 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
4
  require "bigdecimal"
5
5
  require "date"
6
6
 
7
- class Innodb::DataType
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
- # MySQL's Bit-Value Type (BIT).
10
- class BitType
11
- attr_reader :name, :width
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
- 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
18
+ @width = (nbits + 7) / 8
19
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
20
+ end
19
21
 
20
- def value(data)
21
- "0b%b" % BinData::const_get("Uint%dbe" % (@width * 8)).read(data)
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
- class IntegerType
26
- attr_reader :name, :width
27
+ class IntegerType
28
+ attr_reader :name
29
+ attr_reader :width
27
30
 
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
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
- 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
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
- def value(data)
48
- nbits = @width * 8
49
- @unsigned ? get_uint(data, nbits) : get_int(data, nbits)
50
- end
50
+ def value(data)
51
+ nbits = @width * 8
52
+ @unsigned ? get_uint(data, nbits) : get_int(data, nbits)
53
+ end
51
54
 
52
- def get_uint(data, nbits)
53
- BinData::const_get("Uint%dbe" % nbits).read(data)
54
- end
55
+ def get_uint(data, nbits)
56
+ BinData.const_get("Uint%dbe" % nbits).read(data)
57
+ end
55
58
 
56
- def get_int(data, nbits)
57
- BinData::const_get("Int%dbe" % nbits).read(data) ^ (-1 << (nbits - 1))
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
- class FloatType
62
- attr_reader :name, :width
64
+ class FloatType
65
+ attr_reader :name
66
+ attr_reader :width
63
67
 
64
- def initialize(base_type, modifiers, properties)
65
- @width = 4
66
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
67
- end
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
- # Read a little-endian single-precision floating-point number.
70
- def value(data)
71
- BinData::FloatLe.read(data)
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
- class DoubleType
76
- attr_reader :name, :width
79
+ class DoubleType
80
+ attr_reader :name
81
+ attr_reader :width
77
82
 
78
- def initialize(base_type, modifiers, properties)
79
- @width = 8
80
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
81
- end
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
- # Read a little-endian double-precision floating-point number.
84
- def value(data)
85
- BinData::DoubleLe.read(data)
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
- # 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
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
- def value(data)
115
- # Strings representing the integral and fractional parts.
116
- intg, frac = "", ""
120
+ def value(data)
121
+ # Strings representing the integral and fractional parts.
122
+ intg = "".dup
123
+ frac = "".dup
117
124
 
118
- stream = StringIO.new(data)
119
- mask = sign_mask(stream)
125
+ stream = StringIO.new(data)
126
+ mask = sign_mask(stream)
120
127
 
121
- intg << get_digits(stream, mask, @comp_integral)
128
+ intg << get_digits(stream, mask, @comp_integral)
122
129
 
123
- (1 .. @uncomp_integral).each do
124
- intg << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
125
- end
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
- (1 .. @uncomp_fractional).each do
128
- frac << get_digits(stream, mask, MAX_DIGITS_PER_INTEGER)
144
+ BigDecimal(str).to_s("F")
129
145
  end
130
146
 
131
- frac << get_digits(stream, mask, @comp_fractional)
132
- frac = "0" if frac.empty?
147
+ private
133
148
 
134
- # Convert to something resembling a string representation.
135
- str = mask.to_s.chop + intg + '.' + frac
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
- BigDecimal(str).to_s('F')
138
- end
153
+ precision = modifiers.fetch(0, 10)
154
+ raise "Unsupported precision for DECIMAL type" unless precision >= 1 && precision <= 65
139
155
 
140
- private
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
- # The sign is encoded in the high bit of the first byte/digit. The byte
155
- # might be part of a larger integer, so apply the bit-flipper and push
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
- # Return a string representing an integer with a specific number of digits.
168
- def get_digits(stream, mask, digits)
169
- nbits = BYTES_PER_DIGIT[digits] * 8
170
- return "" unless nbits > 0
171
- value = (BinData::const_get("Int%dbe" % nbits).read(stream) ^ mask)
172
- # Preserve leading zeros.
173
- ("%0" + digits.to_s + "d") % value
174
- end
175
- end
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
- # Fixed-length character type.
178
- class CharacterType
179
- attr_reader :name, :width
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
- def initialize(base_type, modifiers, properties)
182
- @width = modifiers.fetch(0, 1)
183
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
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
- def value(data)
187
- # The SQL standard defines that CHAR fields should have end-spaces
188
- # stripped off.
189
- data.sub(/[ ]+$/, "")
190
- end
191
- end
186
+ # Fixed-length character type.
187
+ class CharacterType
188
+ attr_reader :name
189
+ attr_reader :width
192
190
 
193
- class VariableCharacterType
194
- attr_reader :name, :width
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
- def initialize(base_type, modifiers, properties)
197
- @width = modifiers[0]
198
- raise "Invalid width specification" unless modifiers.size == 1
199
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
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
- def value(data)
203
- # The SQL standard defines that VARCHAR fields should have end-spaces
204
- # stripped off.
205
- data.sub(/[ ]+$/, "")
206
- end
207
- end
203
+ class VariableCharacterType
204
+ attr_reader :name
205
+ attr_reader :width
208
206
 
209
- # Fixed-length binary type.
210
- class BinaryType
211
- attr_reader :name, :width
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
- def initialize(base_type, modifiers, properties)
214
- @width = modifiers.fetch(0, 1)
215
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
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
- class VariableBinaryType
220
- attr_reader :name, :width
221
+ # Fixed-length binary type.
222
+ class BinaryType
223
+ attr_reader :name
224
+ attr_reader :width
221
225
 
222
- def initialize(base_type, modifiers, properties)
223
- @width = modifiers[0]
224
- raise "Invalid width specification" unless modifiers.size == 1
225
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
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
- class BlobType
230
- attr_reader :name
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
- def initialize(base_type, modifiers, properties)
233
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
240
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
241
+ end
234
242
  end
235
- end
236
243
 
237
- class YearType
238
- attr_reader :name, :width
244
+ class BlobType
245
+ attr_reader :name
239
246
 
240
- def initialize(base_type, modifiers, properties)
241
- @width = 1
242
- @display_width = modifiers.fetch(0, 4)
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
- def value(data)
247
- year = BinData::Uint8.read(data)
248
- return (year % 100).to_s if @display_width != 4
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
- class TimeType
255
- attr_reader :name, :width
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
- def initialize(base_type, modifiers, properties)
258
- @width = 3
259
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
260
- end
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
- def value(data)
263
- time = BinData::Int24be.read(data) ^ (-1 << 23)
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
- class DateType
271
- attr_reader :name, :width
271
+ class TimeType
272
+ attr_reader :name
273
+ attr_reader :width
272
274
 
273
- def initialize(base_type, modifiers, properties)
274
- @width = 3
275
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
276
- end
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
- def value(data)
279
- date = BinData::Int24be.read(data) ^ (-1 << 23)
280
- day = date & 0x1f
281
- month = (date >> 5) & 0xf
282
- year = date >> 9
283
- "%04d-%02d-%02d" % [year, month, day]
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
- class DatetimeType
288
- attr_reader :name, :width
288
+ class DateType
289
+ attr_reader :name
290
+ attr_reader :width
289
291
 
290
- def initialize(base_type, modifiers, properties)
291
- @width = 8
292
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
293
- end
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
- def value(data)
296
- datetime = BinData::Int64be.read(data) ^ (-1 << 63)
297
- date = datetime / 1000000
298
- year, month, day = [date / 10000, (date / 100) % 100, date % 100]
299
- time = datetime - (date * 1000000)
300
- hour, min, sec = [time / 10000, (time / 100) % 100, time % 100]
301
- "%04d-%02d-%02d %02d:%02d:%02d" % [year, month, day, hour, min, sec]
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
- class TimestampType
306
- attr_reader :name, :width
306
+ class DatetimeType
307
+ attr_reader :name
308
+ attr_reader :width
307
309
 
308
- def initialize(base_type, modifiers, properties)
309
- @width = 4
310
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
311
- end
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
- # Returns the UTC timestamp as a value in 'YYYY-MM-DD HH:MM:SS' format.
314
- def value(data)
315
- timestamp = BinData::Uint32be.read(data)
316
- return "0000-00-00 00:00:00" if timestamp.zero?
317
- DateTime.strptime(timestamp.to_s, '%s').strftime "%Y-%m-%d %H:%M:%S"
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
- # Data types for InnoDB system columns.
323
- #
329
+ class TimestampType
330
+ attr_reader :name
331
+ attr_reader :width
324
332
 
325
- # Transaction ID.
326
- class TransactionIdType
327
- attr_reader :name, :width
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
- def initialize(base_type, modifiers, properties)
330
- @width = 6
331
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
332
- end
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
- def read(c)
335
- c.name("transaction_id") { c.get_uint48 }
343
+ DateTime.strptime(timestamp.to_s, "%s").strftime "%Y-%m-%d %H:%M:%S"
344
+ end
336
345
  end
337
- end
338
346
 
339
- # Rollback data pointer.
340
- class RollPointerType
341
- extend ReadBitsAtOffset
347
+ #
348
+ # Data types for InnoDB system columns.
349
+ #
342
350
 
343
- attr_reader :name, :width
351
+ # Transaction ID.
352
+ class TransactionIdType
353
+ attr_reader :name
354
+ attr_reader :width
344
355
 
345
- def initialize(base_type, modifiers, properties)
346
- @width = 7
347
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
348
- end
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
- def self.parse_roll_pointer(roll_ptr)
351
- {
352
- :is_insert => read_bits_at_offset(roll_ptr, 1, 55) == 1,
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
- def value(data)
362
- roll_ptr = BinData::Uint56be.read(data)
363
- self.class.parse_roll_pointer(roll_ptr)
364
- end
366
+ # Rollback data pointer.
367
+ class RollPointerType
368
+ extend ReadBitsAtOffset
365
369
 
366
- end
370
+ Pointer = Struct.new(
371
+ :is_insert,
372
+ :rseg_id,
373
+ :undo_log,
374
+ keyword_init: true
375
+ )
367
376
 
368
- # Maps base type to data type class.
369
- TYPES = {
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
- def self.new(base_type, modifiers, properties)
413
- raise "Data type '#{base_type}' is not supported" unless TYPES.key?(base_type)
414
- TYPES[base_type].new(base_type, modifiers, properties)
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