innodb_ruby 0.9.14 → 0.12.0

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +654 -778
  5. data/lib/innodb/checksum.rb +26 -24
  6. data/lib/innodb/data_dictionary.rb +490 -550
  7. data/lib/innodb/data_type.rb +362 -325
  8. data/lib/innodb/field.rb +102 -89
  9. data/lib/innodb/fseg_entry.rb +22 -26
  10. data/lib/innodb/history.rb +21 -21
  11. data/lib/innodb/history_list.rb +72 -76
  12. data/lib/innodb/ibuf_bitmap.rb +36 -36
  13. data/lib/innodb/ibuf_index.rb +6 -2
  14. data/lib/innodb/index.rb +245 -276
  15. data/lib/innodb/inode.rb +166 -124
  16. data/lib/innodb/list.rb +196 -183
  17. data/lib/innodb/log.rb +139 -110
  18. data/lib/innodb/log_block.rb +100 -91
  19. data/lib/innodb/log_group.rb +53 -64
  20. data/lib/innodb/log_reader.rb +97 -96
  21. data/lib/innodb/log_record.rb +328 -279
  22. data/lib/innodb/lsn.rb +86 -81
  23. data/lib/innodb/page/blob.rb +82 -83
  24. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  25. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  26. data/lib/innodb/page/index.rb +965 -924
  27. data/lib/innodb/page/index_compressed.rb +34 -34
  28. data/lib/innodb/page/inode.rb +103 -112
  29. data/lib/innodb/page/sys.rb +13 -15
  30. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  31. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  32. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  33. data/lib/innodb/page/trx_sys.rb +204 -182
  34. data/lib/innodb/page/undo_log.rb +106 -92
  35. data/lib/innodb/page.rb +417 -414
  36. data/lib/innodb/record.rb +121 -164
  37. data/lib/innodb/record_describer.rb +66 -68
  38. data/lib/innodb/space.rb +381 -413
  39. data/lib/innodb/stats.rb +33 -35
  40. data/lib/innodb/system.rb +149 -171
  41. data/lib/innodb/undo_log.rb +129 -107
  42. data/lib/innodb/undo_record.rb +255 -247
  43. data/lib/innodb/util/buffer_cursor.rb +81 -79
  44. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  45. data/lib/innodb/version.rb +2 -2
  46. data/lib/innodb/xdes.rb +144 -142
  47. data/lib/innodb.rb +4 -5
  48. metadata +100 -25
@@ -1,415 +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)
147
+ private
132
148
 
133
- # Convert to something resembling a string representation.
134
- 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
135
152
 
136
- BigDecimal.new(str).to_s('F')
137
- end
153
+ precision = modifiers.fetch(0, 10)
154
+ raise "Unsupported precision for DECIMAL type" unless precision >= 1 && precision <= 65
138
155
 
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
156
+ scale = modifiers.fetch(1, 0)
157
+ raise "Unsupported scale for DECIMAL type" unless scale >= 0 && scale <= 30 && scale <= precision
152
158
 
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
159
+ [precision, scale]
160
+ end
165
161
 
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
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
175
174
 
176
- # Fixed-length character type.
177
- class CharacterType
178
- 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?
179
179
 
180
- def initialize(base_type, modifiers, properties)
181
- @width = modifiers.fetch(0, 1)
182
- @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
183
184
  end
184
185
 
185
- def value(data)
186
- # The SQL standard defines that CHAR fields should have end-spaces
187
- # stripped off.
188
- data.sub(/[ ]+$/, "")
189
- end
190
- end
186
+ # Fixed-length character type.
187
+ class CharacterType
188
+ attr_reader :name
189
+ attr_reader :width
191
190
 
192
- class VariableCharacterType
193
- 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
194
195
 
195
- def initialize(base_type, modifiers, properties)
196
- @width = modifiers[0]
197
- raise "Invalid width specification" unless modifiers.size == 1
198
- @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
199
201
  end
200
202
 
201
- def value(data)
202
- # The SQL standard defines that VARCHAR fields should have end-spaces
203
- # stripped off.
204
- data.sub(/[ ]+$/, "")
205
- end
206
- end
203
+ class VariableCharacterType
204
+ attr_reader :name
205
+ attr_reader :width
207
206
 
208
- # Fixed-length binary type.
209
- class BinaryType
210
- 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
211
213
 
212
- def initialize(base_type, modifiers, properties)
213
- @width = modifiers.fetch(0, 1)
214
- @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
215
219
  end
216
- end
217
220
 
218
- class VariableBinaryType
219
- attr_reader :name, :width
221
+ # Fixed-length binary type.
222
+ class BinaryType
223
+ attr_reader :name
224
+ attr_reader :width
220
225
 
221
- def initialize(base_type, modifiers, properties)
222
- @width = modifiers[0]
223
- raise "Invalid width specification" unless modifiers.size == 1
224
- @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
225
230
  end
226
- end
227
231
 
228
- class BlobType
229
- 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
230
239
 
231
- def initialize(base_type, modifiers, properties)
232
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
240
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
241
+ end
233
242
  end
234
- end
235
243
 
236
- class YearType
237
- attr_reader :name, :width
244
+ class BlobType
245
+ attr_reader :name
238
246
 
239
- def initialize(base_type, modifiers, properties)
240
- @width = 1
241
- @display_width = modifiers.fetch(0, 4)
242
- @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
243
250
  end
244
251
 
245
- def value(data)
246
- year = BinData::Uint8.read(data)
247
- return (year % 100).to_s if @display_width != 4
248
- return (year + 1900).to_s if year != 0
249
- "0000"
250
- end
251
- end
252
+ class YearType
253
+ attr_reader :name
254
+ attr_reader :width
252
255
 
253
- class TimeType
254
- 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
255
261
 
256
- def initialize(base_type, modifiers, properties)
257
- @width = 3
258
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
259
- 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
260
266
 
261
- def value(data)
262
- time = BinData::Int24be.read(data) ^ (-1 << 23)
263
- sign = "-" if time < 0
264
- time = time.abs
265
- "%s%02d:%02d:%02d" % [sign, time / 10000, (time / 100) % 100, time % 100]
267
+ "0000"
268
+ end
266
269
  end
267
- end
268
270
 
269
- class DateType
270
- attr_reader :name, :width
271
+ class TimeType
272
+ attr_reader :name
273
+ attr_reader :width
271
274
 
272
- def initialize(base_type, modifiers, properties)
273
- @width = 3
274
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
275
- end
275
+ def initialize(base_type, modifiers, properties)
276
+ @width = 3
277
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
278
+ end
276
279
 
277
- def value(data)
278
- date = BinData::Int24be.read(data) ^ (-1 << 23)
279
- day = date & 0x1f
280
- month = (date >> 5) & 0xf
281
- year = date >> 9
282
- "%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
283
286
  end
284
- end
285
287
 
286
- class DatetimeType
287
- attr_reader :name, :width
288
+ class DateType
289
+ attr_reader :name
290
+ attr_reader :width
288
291
 
289
- def initialize(base_type, modifiers, properties)
290
- @width = 8
291
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
292
- end
292
+ def initialize(base_type, modifiers, properties)
293
+ @width = 3
294
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
295
+ end
293
296
 
294
- def value(data)
295
- datetime = BinData::Int64be.read(data) ^ (-1 << 63)
296
- date = datetime / 1000000
297
- year, month, day = [date / 10000, (date / 100) % 100, date % 100]
298
- time = datetime - (date * 1000000)
299
- hour, min, sec = [time / 10000, (time / 100) % 100, time % 100]
300
- "%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
301
304
  end
302
- end
303
305
 
304
- class TimestampType
305
- attr_reader :name, :width
306
+ class DatetimeType
307
+ attr_reader :name
308
+ attr_reader :width
306
309
 
307
- def initialize(base_type, modifiers, properties)
308
- @width = 4
309
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
310
- end
310
+ def initialize(base_type, modifiers, properties)
311
+ @width = 8
312
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
313
+ end
311
314
 
312
- # Returns the UTC timestamp as a value in 'YYYY-MM-DD HH:MM:SS' format.
313
- def value(data)
314
- timestamp = BinData::Uint32be.read(data)
315
- return "0000-00-00 00:00:00" if timestamp.zero?
316
- 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
317
327
  end
318
- end
319
328
 
320
- #
321
- # Data types for InnoDB system columns.
322
- #
329
+ class TimestampType
330
+ attr_reader :name
331
+ attr_reader :width
323
332
 
324
- # Transaction ID.
325
- class TransactionIdType
326
- 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
327
337
 
328
- def initialize(base_type, modifiers, properties)
329
- @width = 6
330
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
331
- 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?
332
342
 
333
- def read(c)
334
- c.name("transaction_id") { c.get_uint48 }
343
+ DateTime.strptime(timestamp.to_s, "%s").strftime "%Y-%m-%d %H:%M:%S"
344
+ end
335
345
  end
336
- end
337
346
 
338
- # Rollback data pointer.
339
- class RollPointerType
340
- extend ReadBitsAtOffset
347
+ #
348
+ # Data types for InnoDB system columns.
349
+ #
341
350
 
342
- attr_reader :name, :width
351
+ # Transaction ID.
352
+ class TransactionIdType
353
+ attr_reader :name
354
+ attr_reader :width
343
355
 
344
- def initialize(base_type, modifiers, properties)
345
- @width = 7
346
- @name = Innodb::DataType.make_name(base_type, modifiers, properties)
347
- end
356
+ def initialize(base_type, modifiers, properties)
357
+ @width = 6
358
+ @name = Innodb::DataType.make_name(base_type, modifiers, properties)
359
+ end
348
360
 
349
- def self.parse_roll_pointer(roll_ptr)
350
- {
351
- :is_insert => read_bits_at_offset(roll_ptr, 1, 55) == 1,
352
- :rseg_id => read_bits_at_offset(roll_ptr, 7, 48),
353
- :undo_log => {
354
- :page => read_bits_at_offset(roll_ptr, 32, 16),
355
- :offset => read_bits_at_offset(roll_ptr, 16, 0),
356
- }
357
- }
361
+ def read(cursor)
362
+ cursor.name("transaction_id") { cursor.read_uint48 }
363
+ end
358
364
  end
359
365
 
360
- def value(data)
361
- roll_ptr = BinData::Uint56be.read(data)
362
- self.class.parse_roll_pointer(roll_ptr)
363
- end
366
+ # Rollback data pointer.
367
+ class RollPointerType
368
+ extend ReadBitsAtOffset
364
369
 
365
- end
370
+ Pointer = Struct.new(
371
+ :is_insert,
372
+ :rseg_id,
373
+ :undo_log,
374
+ keyword_init: true
375
+ )
366
376
 
367
- # Maps base type to data type class.
368
- TYPES = {
369
- :BIT => BitType,
370
- :BOOL => IntegerType,
371
- :BOOLEAN => IntegerType,
372
- :TINYINT => IntegerType,
373
- :SMALLINT => IntegerType,
374
- :MEDIUMINT => IntegerType,
375
- :INT => IntegerType,
376
- :INT6 => IntegerType,
377
- :BIGINT => IntegerType,
378
- :FLOAT => FloatType,
379
- :DOUBLE => DoubleType,
380
- :DECIMAL => DecimalType,
381
- :NUMERIC => DecimalType,
382
- :CHAR => CharacterType,
383
- :VARCHAR => VariableCharacterType,
384
- :BINARY => BinaryType,
385
- :VARBINARY => VariableBinaryType,
386
- :TINYBLOB => BlobType,
387
- :BLOB => BlobType,
388
- :MEDIUMBLOB => BlobType,
389
- :LONGBLOB => BlobType,
390
- :TINYTEXT => BlobType,
391
- :TEXT => BlobType,
392
- :MEDIUMTEXT => BlobType,
393
- :LONGTEXT => BlobType,
394
- :YEAR => YearType,
395
- :TIME => TimeType,
396
- :DATE => DateType,
397
- :DATETIME => DatetimeType,
398
- :TIMESTAMP => TimestampType,
399
- :TRX_ID => TransactionIdType,
400
- :ROLL_PTR => RollPointerType,
401
- }
402
-
403
- def self.make_name(base_type, modifiers, properties)
404
- name = base_type.to_s
405
- name << '(' + modifiers.join(',') + ')' if not modifiers.empty?
406
- name << " "
407
- name << properties.join(' ')
408
- name.strip
409
- end
377
+ attr_reader :name
378
+ attr_reader :width
410
379
 
411
- def self.new(base_type, modifiers, properties)
412
- raise "Data type '#{base_type}' is not supported" unless TYPES.key?(base_type)
413
- 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
414
451
  end
415
452
  end