innodb_ruby 0.9.16 → 0.11.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 +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