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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4fb0470eeceb5fbe16a858c30310c5046a8d2512
4
- data.tar.gz: f9396fbc702f8b4be752c8300f9dc7cb47317cc2
2
+ SHA256:
3
+ metadata.gz: 21b726dd85c361262f577f663a70b008acb56f45fc2a1632147bc94eb622141e
4
+ data.tar.gz: 720eda759132e67b03c8bcd9c0e50a90625f3de214048e870875cb964bdd8bbe
5
5
  SHA512:
6
- metadata.gz: 001c98d6754675ecfa62f6e93562fc3c398ecfe9f18c869a0685816102dc8d37b0001ebbca317c3aaa0da75616fa5e7669eb537afcad548f301ec61329988f02
7
- data.tar.gz: 2ea7115e3ca1fabfa606789ca645605feca86a4bedb5b1fc3dc5a8369d6bd46b653990eb35d70f0b5ee785f77e6983ef9609ebc7e508c89cd0174bdffc1523bf
6
+ metadata.gz: 1814840cf61fd850140e282ea2706c196563881e4de69e6612e890b8dcd6e9b93cd4dbc73c61d8c02fbc071c3e339223eea4d0af636ef00a4505ab1f143e207e
7
+ data.tar.gz: 85b0326cc2b029ab24a493e1b781e4f9239533bff9b1849e67a4672493ce9775006a08004ce1a4d9aa724b0324354c67465cfcdfa3a722032c7c34f90822c6c1
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- # A parser for InnoDB file formats, in Ruby #
1
+ # A parser for InnoDB file formats, in Ruby
2
+
3
+ [![rspec test status](https://github.com/jeremycole/innodb_ruby/actions/workflows/rspec.yml/badge.svg)](https://github.com/jeremycole/innodb_ruby/actions/workflows/rspec.yml)
4
+ [![rubocop style check status](https://github.com/jeremycole/innodb_ruby/actions/workflows/rubocop.yml/badge.svg)](https://github.com/jeremycole/innodb_ruby/actions/workflows/rubocop.yml)
2
5
 
3
6
  The purpose for this library and tools is to expose some otherwise hidden internals of InnoDB. This code is not intended for critical production usage. It is definitely buggy, and it may be dangerous. Neither its internal APIs or its output are considered stable and are subject to change at any time.
4
7
 
@@ -11,11 +14,7 @@ It is intended as for a few purposes:
11
14
 
12
15
  Various parts of this library and the tools included may have wildly differing maturity levels, as it is worked on primarily based on immediate needs of the authors.
13
16
 
14
- # Resources #
17
+ # Resources
15
18
 
16
19
  * The [innodb_ruby wiki](https://github.com/jeremycole/innodb_ruby/wiki) contains some additional references and documentation to help you get started.
17
- * Visit the [innodb_ruby mailing list on Google Groups](https://groups.google.com/d/forum/innodb_ruby) or email [innodb_ruby@googlegroups.com](mailto:innodb_ruby@googlegroups.com) — If you have questions about `innodb_ruby` or its usage.
18
20
  * See the [RubyGems page for innodb_ruby](http://rubygems.org/gems/innodb_ruby) — Gem packaged releases are published regularly to RubyGems.org, which also provides online documentation.
19
-
20
-
21
- [![Build Status](https://travis-ci.org/jeremycole/innodb_ruby.svg?branch=master)](https://travis-ci.org/jeremycole/innodb_ruby)
data/bin/innodb_log CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
3
3
 
4
4
  require "getoptlong"
5
5
  require "ostruct"
@@ -7,15 +7,9 @@ require "set"
7
7
  require "innodb"
8
8
 
9
9
  def log_summary(log_group)
10
- puts "%-20s%-15s%-10s%-12s%-10s" % [
11
- "lsn",
12
- "block",
13
- "length",
14
- "first_rec",
15
- "checkpoint",
16
- ]
10
+ puts "%-20s%-15s%-10s%-12s%-10s" % %w[lsn block length first_rec checkpoint]
17
11
  lsn = log_group.start_lsn.first
18
- log_group.each_block do |block_index, block|
12
+ log_group.each_block do |_block_index, block|
19
13
  header = block.header
20
14
  puts "%-20i%-15i%-10i%-12i%-10i" % [
21
15
  lsn,
@@ -30,14 +24,7 @@ def log_summary(log_group)
30
24
  end
31
25
 
32
26
  def log_reader_record_summary(reader, follow)
33
- puts "%-10s%-10s%-20s%-10s%-10s%-10s" % [
34
- "time",
35
- "lsn",
36
- "type",
37
- "size",
38
- "space",
39
- "page"
40
- ]
27
+ puts "%-10s%-10s%-20s%-10s%-10s%-10s" % %w[time lsn type size space page]
41
28
 
42
29
  reader.each_record(follow) do |rec|
43
30
  preamble = rec.preamble.dup
@@ -72,6 +59,7 @@ end
72
59
  def usage(exit_code, message = nil)
73
60
  print "Error: #{message}\n" unless message.nil?
74
61
 
62
+ # rubocop:disable Layout/HeredocIndentation
75
63
  print <<'END_OF_USAGE'
76
64
 
77
65
  Usage: innodb_log [-d] [-l <lsn>] -f <log file> <mode>
@@ -103,6 +91,7 @@ The following modes are supported:
103
91
  Dump the contents of a log record, using the Ruby pp ("pretty-print") module.
104
92
 
105
93
  END_OF_USAGE
94
+ # rubocop:enable Layout/HeredocIndentation
106
95
 
107
96
  exit exit_code
108
97
  end
@@ -112,12 +101,14 @@ end
112
101
  @options.dump = false
113
102
  @options.lsn = nil
114
103
 
104
+ # rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
115
105
  getopt_options = [
116
106
  [ "--help", "-?", GetoptLong::NO_ARGUMENT ],
117
107
  [ "--log-file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
118
108
  [ "--dump-blocks", "-d", GetoptLong::NO_ARGUMENT ],
119
109
  [ "--lsn", "-l", GetoptLong::REQUIRED_ARGUMENT ],
120
110
  ]
111
+ # rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
121
112
 
122
113
  getopt = GetoptLong.new(*getopt_options)
123
114
 
@@ -136,6 +127,8 @@ end
136
127
 
137
128
  mode = ARGV.shift
138
129
 
130
+ # rubocop:disable Style/IfUnlessModifier
131
+
139
132
  unless mode
140
133
  usage 1, "At least one mode must be provided"
141
134
  end
@@ -144,10 +137,12 @@ if @options.log_files.empty?
144
137
  usage 1, "At least one log file (-f) must be specified"
145
138
  end
146
139
 
147
- if /^(log-)?record-/.match(mode) and !@options.lsn
140
+ if /^(log-)?record-/.match(mode) && !@options.lsn
148
141
  usage 1, "LSN must be specified using -l/--lsn"
149
142
  end
150
143
 
144
+ # rubocop:enable Style/IfUnlessModifier
145
+
151
146
  log_group = Innodb::LogGroup.new(@options.log_files.sort)
152
147
 
153
148
  case mode
data/bin/innodb_space CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- # -*- encoding : utf-8 -*-
2
+ # frozen_string_literal: true
3
3
 
4
4
  require "getoptlong"
5
5
  require "ostruct"
6
+ require "histogram/array"
6
7
  require "innodb"
7
8
 
8
9
  # Convert a floating point RGB array into an ANSI color number approximating it.
@@ -11,17 +12,13 @@ def rgb_to_ansi(rgb)
11
12
  16 + (rgb_n[0] * 36) + (rgb_n[1] * 6) + rgb_n[2]
12
13
  end
13
14
 
14
- def rgb_to_rgbhex(rgb)
15
- rgb.map { |c| "%02x" % [(c * 255.0).round] }.join
16
- end
17
-
18
15
  # Interpolate intermediate float-arrays between two float-arrays. Do not
19
16
  # include the points a and b in the result.
20
- def interpolate(a, b, count)
21
- deltas = a.each_index.map { |i| b[i] - a[i] }
22
- steps = a.each_index.map { |i| deltas[i].to_f / (count.to_f + 1) }
17
+ def interpolate(ary_a, ary_b, count)
18
+ deltas = ary_a.each_index.map { |i| ary_b[i] - ary_a[i] }
19
+ steps = ary_a.each_index.map { |i| deltas[i].to_f / (count.to_f + 1) }
23
20
 
24
- count.times.to_a.map { |i| a.each_index.map { |j| a[j] + ((i+1).to_f * steps[j]) } }
21
+ count.times.to_a.map { |i| ary_a.each_index.map { |j| ary_a[j] + ((i + 1).to_f * steps[j]) } }
25
22
  end
26
23
 
27
24
  # Interpolate intermediate float-arrays between each step in a sequence of
@@ -29,7 +26,7 @@ end
29
26
  def interpolate_sequence(sequence, count)
30
27
  result = []
31
28
  result << sequence.first
32
- (sequence.size-1).times.map { |n| [sequence[n], sequence[n+1]] }.each do |from, to|
29
+ (sequence.size - 1).times.map { |n| [sequence[n], sequence[n + 1]] }.each do |from, to|
33
30
  interpolate(from, to, count).each do |step|
34
31
  result << step
35
32
  end
@@ -48,15 +45,11 @@ HEATMAP_PROGRESSION = [
48
45
  [1.0, 1.0, 0.0], # Yellow
49
46
  [1.0, 0.0, 0.0], # Red
50
47
  [1.0, 0.0, 1.0], # Purple
51
- ]
48
+ ].freeze
52
49
 
53
50
  # Typical heatmap color progression.
54
51
  ANSI_COLORS_HEATMAP = interpolate_sequence(HEATMAP_PROGRESSION, 6).map { |rgb| rgb_to_ansi(rgb) }
55
52
 
56
- RGBHEX_COLORS_HEATMAP = interpolate_sequence(HEATMAP_PROGRESSION, 41).map { |rgb| rgb_to_rgbhex(rgb) }
57
-
58
- RGBHEX_COLORS_RANDOM = 100.times.inject([]) { |a, x| a << rgb_to_rgbhex([rand * 0.7 + 0.25, rand * 0.7 + 0.25, rand * 0.7 + 0.25]) }
59
-
60
53
  # The 24-step grayscale progression.
61
54
  ANSI_COLORS_GRAYSCALE = (0xe8..0xff).to_a
62
55
 
@@ -66,69 +59,47 @@ def ansi_color(color, text)
66
59
  end
67
60
 
68
61
  # Zero and 1/8 through 8/8 illustrations.
69
- BLOCK_CHARS_V = ["", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
70
- BLOCK_CHARS_H = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"]
62
+ BLOCK_CHARS_V = "░▁▂▃▄▅▆▇█".chars.freeze
63
+ BLOCK_CHARS_H = "░▏▎▍▌▋▊▉█".chars.freeze
71
64
 
72
65
  # A reasonably large prime number to multiply identifiers by in order to
73
66
  # space out the colors used for similar identifiers.
74
- COLOR_SPACING_PRIME = 999983
67
+ COLOR_SPACING_PRIME = 999_983
75
68
 
76
69
  # Return a string with a possibly colored filled block for use in printing
77
70
  # to an ANSI-capable Unicode-enabled terminal.
78
- def filled_block(fraction, identifier=nil, block_chars=BLOCK_CHARS_V)
79
- if fraction == 0.0
71
+ def filled_block(fraction, identifier = nil, block_chars = BLOCK_CHARS_V)
72
+ if fraction.zero?
80
73
  block = block_chars[0]
81
74
  else
82
- parts = (fraction.to_f * (block_chars.size.to_f-1)).floor
83
- block = block_chars[[block_chars.size-1, parts+1].min]
75
+ parts = (fraction.to_f * (block_chars.size.to_f - 1)).floor
76
+ block = block_chars[[block_chars.size - 1, parts + 1].min]
84
77
  end
85
78
  if identifier
86
79
  # ANSI 256-color mode, color palette starts at 10 and contains 216 colors.
87
- color = 16 + ((identifier * COLOR_SPACING_PRIME) % 216)
80
+ color = 16 + ((identifier * COLOR_SPACING_PRIME) % 216)
88
81
  ansi_color(color, block)
89
82
  else
90
83
  block
91
84
  end
92
85
  end
93
86
 
94
- def svg_join_args(args)
95
- args.map { |arg| "%s=\"%s\"" % [ arg[0], arg[1], ] }.join(" ")
96
- end
87
+ def center(text, width)
88
+ return text if text.size >= width
97
89
 
98
- def svg(elem, args, content=nil)
99
- if content
100
- "<" + elem.to_s + " " + svg_join_args(args) + ">" +
101
- content.to_s +
102
- "</" + elem.to_s + ">"
103
- else
104
- "<" + elem.to_s + " " + svg_join_args(args) + " />"
105
- end
106
- end
107
-
108
- def svg_path_rounded_rect(x, y, w, h, r)
109
- [
110
- "M%i,%i" % [ x + r, y - r ],
111
- "L%i,%i" % [ x + w - r, y - r ],
112
- "S%i,%i %i,%i" % [ x + w + r, y - r, x + w + r, y + r ],
113
- "L%i,%i" % [ x + w + r, y + h - r ],
114
- "S%i,%i %i,%i" % [ x + w + r, y + h + r, x + w - r, y + h + r ],
115
- "L%i,%i" % [ x + r, y + h + r ],
116
- "S%i,%i %i,%i" % [ x - r, y + h + r, x - r, y + h - r ],
117
- "L%i,%i" % [ x - r, y + r ],
118
- "S%i,%i %i,%i" % [ x - r, y - r, x + r, y - r ],
119
- "Z",
120
- ].join(" ")
90
+ spaces = (width - text.size) / 2
91
+ (" " * spaces) + text + (" " * spaces)
121
92
  end
122
93
 
123
94
  # Print metadata about each list in an array of InnoDB::List objects.
124
95
  def print_lists(lists)
125
- puts "%-20s%-12s%-12s%-12s%-12s%-12s" % [
126
- "name",
127
- "length",
128
- "f_page",
129
- "f_offset",
130
- "l_page",
131
- "l_offset",
96
+ puts "%-20s%-12s%-12s%-12s%-12s%-12s" % %w[
97
+ name
98
+ length
99
+ f_page
100
+ f_offset
101
+ l_page
102
+ l_offset
132
103
  ]
133
104
 
134
105
  lists.each do |name, list|
@@ -146,33 +117,31 @@ end
146
117
  # Print a page usage bitmap for each extent descriptor in an array of
147
118
  # Innodb::XdesEntry objects.
148
119
  def print_xdes_list(space, list)
149
- puts "%-12s%-64s" % [
150
- "start_page",
151
- "page_used_bitmap"
120
+ puts "%-12s%-64s" % %w[
121
+ start_page
122
+ page_used_bitmap
152
123
  ]
153
124
 
154
125
  list.each do |entry|
155
- puts "%-12i%-64s" % [
156
- entry.xdes[:start_page],
157
- entry.each_page_status.inject("") { |bitmap, (page_number, page_status)|
158
- if page_number < space.pages
159
- bitmap += page_status[:free] ? "." : "#"
160
- end
161
- bitmap
162
- },
163
- ]
126
+ display = entry.each_page_status.inject("") do |bitmap, (page_number, page_status)|
127
+ if page_number < space.pages
128
+ bitmap += page_status[:free] ? "." : "#"
129
+ end
130
+ bitmap
131
+ end
132
+ puts "%-12i%-64s" % [entry.xdes[:start_page], display]
164
133
  end
165
134
  end
166
135
 
167
136
  # Print a summary of page usage for all pages in an index.
168
137
  def print_index_page_summary(pages)
169
- puts "%-12s%-8s%-8s%-8s%-8s%-8s" % [
170
- "page",
171
- "index",
172
- "level",
173
- "data",
174
- "free",
175
- "records",
138
+ puts "%-12s%-8s%-8s%-8s%-8s%-8s" % %w[
139
+ page
140
+ index
141
+ level
142
+ data
143
+ free
144
+ records
176
145
  ]
177
146
 
178
147
  pages.each do |page_number, page|
@@ -187,17 +156,17 @@ def print_index_page_summary(pages)
187
156
  page.records,
188
157
  ]
189
158
  when :ALLOCATED
190
- puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [ page_number, 0, 0, 0, page.size, 0 ]
159
+ puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [page_number, 0, 0, 0, page.size, 0]
191
160
  end
192
161
  end
193
162
  end
194
163
 
195
164
  # Print a summary of all spaces in the InnoDB system.
196
165
  def system_spaces(innodb_system)
197
- puts "%-32s%-12s%-12s" % [
198
- "name",
199
- "pages",
200
- "indexes",
166
+ puts "%-32s%-12s%-12s" % %w[
167
+ name
168
+ pages
169
+ indexes
201
170
  ]
202
171
 
203
172
  print_space_information = lambda do |name, space|
@@ -213,6 +182,7 @@ def system_spaces(innodb_system)
213
182
  innodb_system.each_table_name do |table_name|
214
183
  space = innodb_system.space_by_table_name(table_name)
215
184
  next unless space
185
+
216
186
  print_space_information.call(table_name, space)
217
187
  end
218
188
 
@@ -223,15 +193,15 @@ end
223
193
 
224
194
  # Print the contents of the SYS_TABLES data dictionary table.
225
195
  def data_dictionary_tables(innodb_system)
226
- puts "%-32s%-12s%-12s%-12s%-12s%-12s%-15s%-12s" % [
227
- "name",
228
- "id",
229
- "n_cols",
230
- "type",
231
- "mix_id",
232
- "mix_len",
233
- "cluster_name",
234
- "space",
196
+ puts "%-32s%-12s%-12s%-12s%-12s%-12s%-15s%-12s" % %w[
197
+ name
198
+ id
199
+ n_cols
200
+ type
201
+ mix_id
202
+ mix_len
203
+ cluster_name
204
+ space
235
205
  ]
236
206
 
237
207
  innodb_system.data_dictionary.each_table do |record|
@@ -250,14 +220,14 @@ end
250
220
 
251
221
  # Print the contents of the SYS_COLUMNS data dictionary table.
252
222
  def data_dictionary_columns(innodb_system)
253
- puts "%-12s%-6s%-32s%-12s%-12s%-6s%-6s" % [
254
- "table_id",
255
- "pos",
256
- "name",
257
- "mtype",
258
- "prtype",
259
- "len",
260
- "prec",
223
+ puts "%-12s%-6s%-32s%-12s%-12s%-6s%-6s" % %w[
224
+ table_id
225
+ pos
226
+ name
227
+ mtype
228
+ prtype
229
+ len
230
+ prec
261
231
  ]
262
232
 
263
233
  innodb_system.data_dictionary.each_column do |record|
@@ -275,14 +245,14 @@ end
275
245
 
276
246
  # Print the contents of the SYS_INDEXES data dictionary table.
277
247
  def data_dictionary_indexes(innodb_system)
278
- puts "%-12s%-12s%-32s%-10s%-6s%-12s%-12s" % [
279
- "table_id",
280
- "id",
281
- "name",
282
- "n_fields",
283
- "type",
284
- "space",
285
- "page_no",
248
+ puts "%-12s%-12s%-32s%-10s%-6s%-12s%-12s" % %w[
249
+ table_id
250
+ id
251
+ name
252
+ n_fields
253
+ type
254
+ space
255
+ page_no
286
256
  ]
287
257
 
288
258
  innodb_system.data_dictionary.each_index do |record|
@@ -300,10 +270,10 @@ end
300
270
 
301
271
  # Print the contents of the SYS_FIELDS data dictionary table.
302
272
  def data_dictionary_fields(innodb_system)
303
- puts "%-12s%-12s%-32s" % [
304
- "index_id",
305
- "pos",
306
- "col_name",
273
+ puts "%-12s%-12s%-32s" % %w[
274
+ index_id
275
+ pos
276
+ col_name
307
277
  ]
308
278
 
309
279
  innodb_system.data_dictionary.each_field do |record|
@@ -316,12 +286,12 @@ def data_dictionary_fields(innodb_system)
316
286
  end
317
287
 
318
288
  def space_summary(space, start_page)
319
- puts "%-12s%-20s%-12s%-12s%-20s" % [
320
- "page",
321
- "type",
322
- "prev",
323
- "next",
324
- "lsn",
289
+ puts "%-12s%-20s%-12s%-12s%-20s" % %w[
290
+ page
291
+ type
292
+ prev
293
+ next
294
+ lsn
325
295
  ]
326
296
 
327
297
  space.each_page(start_page) do |page_number, page|
@@ -344,11 +314,11 @@ def space_index_fseg_pages_summary(space, fseg_id)
344
314
  end
345
315
 
346
316
  def space_page_type_regions(space, start_page)
347
- puts "%-12s%-12s%-12s%-20s" % [
348
- "start",
349
- "end",
350
- "count",
351
- "type",
317
+ puts "%-12s%-12s%-12s%-20s" % %w[
318
+ start
319
+ end
320
+ count
321
+ type
352
322
  ]
353
323
 
354
324
  space.each_page_type_region(start_page) do |region|
@@ -367,16 +337,16 @@ def space_page_type_summary(space, start_page)
367
337
  page_count = 0
368
338
  # A Hash of page type => count.
369
339
  page_type = Hash.new(0)
370
- space.each_page(start_page) do |page_number, page|
340
+ space.each_page(start_page) do |_page_number, page|
371
341
  page_count += 1
372
342
  page_type[page.type] += 1
373
343
  end
374
344
 
375
- puts "%-20s%-12s%-12s%-20s" % [
376
- "type",
377
- "count",
378
- "percent",
379
- "description",
345
+ puts "%-20s%-12s%-12s%-20s" % %w[
346
+ type
347
+ count
348
+ percent
349
+ description
380
350
  ]
381
351
 
382
352
  # Sort the page type Hash by count, descending.
@@ -384,7 +354,7 @@ def space_page_type_summary(space, start_page)
384
354
  puts "%-20s%-12i%-12.2f%-20s" % [
385
355
  type,
386
356
  type_count,
387
- 100.0 * (type_count.to_f / page_count.to_f),
357
+ 100.0 * (type_count.to_f / page_count),
388
358
  Innodb::Page::PAGE_TYPE[type][:description],
389
359
  ]
390
360
  end
@@ -397,16 +367,14 @@ end
397
367
  def space_list_iterate(space, list_name)
398
368
  fsp = space.page(0).fsp_header
399
369
 
400
- unless fsp[list_name] && fsp[list_name].is_a?(Innodb::List)
401
- raise "List '#{list_name}' doesn't exist"
402
- end
370
+ raise "List '#{list_name}' doesn't exist" unless fsp[list_name].is_a?(Innodb::List)
403
371
 
404
372
  case fsp[list_name]
405
373
  when Innodb::List::Xdes
406
374
  print_xdes_list(space, fsp[list_name])
407
375
  when Innodb::List::Inode
408
- puts "%-12s" % [
409
- "page",
376
+ puts "%-12s" % %w[
377
+ page
410
378
  ]
411
379
  fsp[list_name].each do |page|
412
380
  puts "%-12i" % [
@@ -417,15 +385,15 @@ def space_list_iterate(space, list_name)
417
385
  end
418
386
 
419
387
  def space_indexes(innodb_system, space)
420
- puts "%-12s%-32s%-12s%-12s%-12s%-12s%-12s%-12s" % [
421
- "id",
422
- "name",
423
- "root",
424
- "fseg",
425
- "fseg_id",
426
- "used",
427
- "allocated",
428
- "fill_factor",
388
+ puts "%-12s%-32s%-12s%-12s%-12s%-12s%-12s%-12s" % %w[
389
+ id
390
+ name
391
+ root
392
+ fseg
393
+ fseg_id
394
+ used
395
+ allocated
396
+ fill_factor
429
397
  ]
430
398
 
431
399
  space.each_index do |index|
@@ -444,17 +412,15 @@ def space_indexes(innodb_system, space)
444
412
  end
445
413
  end
446
414
 
447
- def space_index_pages_free_plot(space, image, start_page)
448
- unless require "gnuplot"
449
- raise "Couldn't load gnuplot. Is it installed?"
450
- end
415
+ def space_index_pages_free_plot(space, start_page)
416
+ raise "Could not load gnuplot. Is it installed?" unless require "gnuplot"
451
417
 
452
- index_data = {0 => {:x => [], :y => []}}
418
+ index_data = { 0 => { x: [], y: [] } }
453
419
 
454
420
  space.each_page(start_page) do |page_number, page|
455
421
  case page.type
456
422
  when :INDEX
457
- data = (index_data[page.page_header[:index_id]] ||= {:x => [], :y => []})
423
+ data = (index_data[page.page_header[:index_id]] ||= { x: [], y: [] })
458
424
  data[:x] << page_number
459
425
  data[:y] << page.free_space
460
426
  when :ALLOCATED
@@ -463,15 +429,17 @@ def space_index_pages_free_plot(space, image, start_page)
463
429
  end
464
430
  end
465
431
 
466
- image_file = image + "_free.png"
432
+ image_name = space.name.sub(".ibd", "").gsub(/[^a-zA-Z0-9_]/, "_").sub(/\A_+/, "")
433
+ image_file = "#{image_name}_free.png"
434
+
467
435
  # Aim for one horizontal pixel per extent, but min 1k and max 10k width.
468
- image_width = [10000, [1000, space.pages / space.pages_per_extent].max].min
436
+ image_width = [10_000, [1_000, space.pages / space.pages_per_extent].max].min
469
437
 
470
438
  Gnuplot.open do |gp|
471
439
  Gnuplot::Plot.new(gp) do |plot|
472
440
  plot.terminal "png size #{image_width}, 800"
473
441
  plot.output image_file
474
- plot.title image
442
+ plot.title image_name.gsub("_", " ")
475
443
  plot.key "reverse left top box horizontal Left textcolor variable"
476
444
  plot.ylabel "free space per page"
477
445
  plot.xlabel "page number"
@@ -481,7 +449,7 @@ def space_index_pages_free_plot(space, image, start_page)
481
449
  index_data.sort.each do |id, data|
482
450
  plot.data << Gnuplot::DataSet.new([data[:x], data[:y]]) do |ds|
483
451
  ds.with = "dots"
484
- ds.title = id == 0 ? "Unallocated" : "Index #{id}"
452
+ ds.title = id.zero? ? "Unallocated" : "Index #{id}"
485
453
  end
486
454
  end
487
455
 
@@ -494,279 +462,99 @@ def space_extents(space)
494
462
  print_xdes_list(space, space.each_xdes)
495
463
  end
496
464
 
465
+ # rubocop:disable Metrics/BlockNesting
466
+ def space_extents_illustrate_page_status(space, entry, count_by_identifier, identifiers)
467
+ entry.each_page_status.each_with_object("".dup) do |(page_number, page_status), bitmap|
468
+ if page_number >= space.pages
469
+ bitmap << " "
470
+ next
471
+ end
472
+
473
+ used_fraction = 1.0
474
+ identifier = nil
475
+ if page_status[:free]
476
+ used_fraction = 0.0
477
+ else
478
+ page = space.page(page_number)
479
+ used_fraction = page.used_space.to_f / page.size if page.respond_to?(:used_space)
480
+ if page.respond_to?(:index_id)
481
+ identifier = page.index_id
482
+ unless identifiers[identifier]
483
+ identifiers[identifier] = page.ibuf_index? ? "Insert Buffer Index" : "Index #{page.index_id}"
484
+ if space.innodb_system
485
+ table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
486
+ identifiers[identifier] += " (%s.%s)" % [table, index] if table && index
487
+ end
488
+ end
489
+ end
490
+ end
491
+
492
+ bitmap << filled_block(used_fraction, identifier)
493
+
494
+ if used_fraction.zero?
495
+ count_by_identifier[:free] += 1
496
+ else
497
+ count_by_identifier[identifier] += 1
498
+ end
499
+ end
500
+ end
501
+ # rubocop:enable Metrics/BlockNesting
502
+
497
503
  # Illustrate the space by printing each extent and for each page, printing a
498
504
  # filled block colored based on the index the page is part of. Print a legend
499
505
  # for the colors used afterwards.
500
506
  def space_extents_illustrate(space)
501
- line_width = space.pages_per_extent
507
+ width = space.pages_per_extent
502
508
  puts
503
- puts "%12s ╭%-#{line_width}s" % [ "Start Page", "─" * line_width ]
509
+ puts "%12s %-#{width}s " % ["", center(space.name, width)]
510
+ puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
504
511
 
505
512
  identifiers = {}
506
513
  count_by_identifier = Hash.new(0)
507
514
 
508
515
  space.each_xdes do |entry|
509
- puts "%12i │%-#{line_width}s│" % [
516
+ puts "%12i │%-#{width}s│" % [
510
517
  entry.xdes[:start_page],
511
- entry.each_page_status.inject("") { |bitmap, (page_number, page_status)|
512
- if page_number < space.pages
513
- used_fraction = 1.0
514
- identifier = nil
515
- if page_status[:free]
516
- used_fraction = 0.0
517
- else
518
- page = space.page(page_number)
519
- if page.respond_to?(:used_space)
520
- used_fraction = page.used_space.to_f / page.size.to_f
521
- end
522
- if page.respond_to?(:index_id)
523
- identifier = page.index_id
524
- unless identifiers[identifier]
525
- identifiers[identifier] = (page.index_id == Innodb::IbufIndex::INDEX_ID) ?
526
- "Insert Buffer Index" :
527
- "Index #{page.index_id}"
528
- if space.innodb_system
529
- table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
530
- if table && index
531
- identifiers[identifier] += " (%s.%s)" % [table, index]
532
- end
533
- end
534
- end
535
- end
536
- end
537
- bitmap += filled_block(used_fraction, identifier)
538
- if used_fraction != 0.0
539
- count_by_identifier[identifier] += 1
540
- else
541
- count_by_identifier[:free] += 1
542
- end
543
- else
544
- bitmap += " "
545
- end
546
- bitmap
547
- },
518
+ space_extents_illustrate_page_status(space, entry, count_by_identifier, identifiers),
548
519
  ]
549
520
  end
550
521
  total_pages = count_by_identifier.values.reduce(:+)
551
522
 
552
- puts "%12s ╰%-#{line_width}s╯" % [ "", "─" * line_width ]
523
+ puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
553
524
 
554
525
  puts
555
526
  puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
556
527
  puts " %-62s %8s %8s" % [
557
- "Page Type", "Pages", "Ratio"
528
+ "Page Type",
529
+ "Pages",
530
+ "Ratio",
558
531
  ]
559
532
  puts " %s %-60s %8i %7.2f%%" % [
560
533
  filled_block(1.0, nil),
561
534
  "System",
562
535
  count_by_identifier[nil],
563
- 100.0 * (count_by_identifier[nil].to_f / total_pages.to_f),
536
+ 100.0 * (count_by_identifier[nil].to_f / total_pages),
564
537
  ]
565
538
  identifiers.sort.each do |identifier, description|
566
539
  puts " %s %-60s %8i %7.2f%%" % [
567
540
  filled_block(1.0, identifier),
568
541
  description,
569
542
  count_by_identifier[identifier],
570
- 100.0 * (count_by_identifier[identifier].to_f / total_pages.to_f),
543
+ 100.0 * (count_by_identifier[identifier].to_f / total_pages),
571
544
  ]
572
545
  end
573
546
  puts " %s %-60s %8i %7.2f%%" % [
574
547
  filled_block(0.0, nil),
575
548
  "Free space",
576
549
  count_by_identifier[:free],
577
- 100.0 * (count_by_identifier[:free].to_f / total_pages.to_f),
550
+ 100.0 * (count_by_identifier[:free].to_f / total_pages),
578
551
  ]
579
552
  puts
580
553
  end
581
554
 
582
- def svg_extent_legend(x, y, block_size, color=nil, description=nil, pages=nil, ratio=nil)
583
- [
584
- svg("rect", {
585
- "y" => y,
586
- "x" => x,
587
- "width" => block_size,
588
- "height" => block_size,
589
- "fill" => color ? color : "white",
590
- "stroke" => description ? "black" : "none",
591
- }),
592
- svg("text", {
593
- "y" => y + block_size - 4,
594
- "x" => x + (description ? block_size + 5 : 0),
595
- "font-family" => "monospace",
596
- "font-size" => block_size,
597
- "font-weight" => description ? "normal" : "bold",
598
- "text-anchor" => "start",
599
- }, description ? description : "Page Type"),
600
- svg("text", {
601
- "y" => y + block_size - 4,
602
- "x" => x + block_size + 5 + (40 * block_size),
603
- "font-family" => "monospace",
604
- "font-size" => block_size,
605
- "font-weight" => description ? "normal" : "bold",
606
- "text-anchor" => "end",
607
- }, pages ? pages : "Pages"),
608
- svg("text", {
609
- "y" => y + block_size - 4,
610
- "x" => x + block_size + 5 + (40 * block_size) + (10 * block_size),
611
- "font-family" => "monospace",
612
- "font-size" => block_size,
613
- "font-weight" => description ? "normal" : "bold",
614
- "text-anchor" => "end",
615
- }, ratio ? ("%7.2f%%" % [ratio]) : "Ratio"),
616
- ].join("\n")
617
- end
618
-
619
- # Illustrate the space by printing each extent and for each page, printing a
620
- # filled block colored based on the index the page is part of. Print a legend
621
- # for the colors used afterwards.
622
- def space_extents_illustrate_svg(space)
623
- line_width = space.pages_per_extent
624
- block_size = @options.illustration_block_size
625
-
626
- puts "<?xml version=\"1.0\"?>"
627
- puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
628
-
629
- identifiers = {}
630
- count_by_identifier = Hash.new(0)
631
-
632
- graphic_x = 48
633
- graphic_y = 16
634
-
635
- puts svg("text", {
636
- "y" => graphic_y - 3,
637
- "x" => graphic_x - 7,
638
- "font-family" => "monospace",
639
- "font-size" => block_size,
640
- "font-weight" => "bold",
641
- "text-anchor" => "end",
642
- }, "Page")
643
-
644
- block_x = 0
645
- block_y = 0
646
- space.each_xdes do |entry|
647
- block_x = 0
648
-
649
- puts svg("text", {
650
- "y" => graphic_y + block_y + block_size,
651
- "x" => graphic_x - 7,
652
- "font-family" => "monospace",
653
- "font-size" => block_size,
654
- "text-anchor" => "end",
655
- }, entry.xdes[:start_page])
656
-
657
- entry.each_page_status do |page_number, page_status|
658
- if page_number < space.pages
659
- used_fraction = 1.0
660
- identifier = nil
661
- if page_status[:free]
662
- used_fraction = 0.0
663
- else
664
- page = space.page(page_number)
665
- if page.respond_to?(:used_space)
666
- used_fraction = page.used_space.to_f / page.size.to_f
667
- end
668
- if page.respond_to?(:index_id)
669
- identifier = page.index_id
670
- unless identifiers[identifier]
671
- identifiers[identifier] = (page.index_id == Innodb::IbufIndex::INDEX_ID) ?
672
- "Insert Buffer Index" :
673
- "Index #{page.index_id}"
674
- if space.innodb_system
675
- table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
676
- if table && index
677
- identifiers[identifier] += " (%s.%s)" % [table, index]
678
- end
679
- end
680
- end
681
- end
682
- end
683
- if used_fraction != 0.0
684
- count_by_identifier[identifier] += 1
685
- else
686
- count_by_identifier[:free] += 1
687
- end
688
-
689
- block_height = block_size * used_fraction
690
- color = "black"
691
- if identifier
692
- color = "#" + RGBHEX_COLORS_RANDOM[(identifier * COLOR_SPACING_PRIME) % RGBHEX_COLORS_RANDOM.size]
693
- end
694
- puts svg("rect", {
695
- "x" => graphic_x + block_x,
696
- "y" => graphic_y + block_y + (block_size - block_height),
697
- "width" => block_size,
698
- "height" => block_height,
699
- "fill" => color,
700
- })
701
- end
702
- block_x += block_size
703
- end
704
- block_y += block_size
705
- end
706
-
707
- puts svg("path", {
708
- "stroke" => "black",
709
- "stroke-width" => 1,
710
- "fill" => "none",
711
- "d" => svg_path_rounded_rect(
712
- graphic_x,
713
- graphic_y,
714
- block_x,
715
- block_y,
716
- 4
717
- ),
718
- })
719
-
720
- block_x = 0
721
- block_y += 10
722
- puts svg_extent_legend(
723
- graphic_x + block_x,
724
- graphic_y + block_y,
725
- block_size
726
- )
727
- block_y += block_size + 2
728
-
729
- puts svg_extent_legend(
730
- graphic_x + block_x,
731
- graphic_y + block_y,
732
- block_size,
733
- "black",
734
- "System",
735
- count_by_identifier[nil],
736
- 100.0 * (count_by_identifier[nil].to_f / space.pages.to_f)
737
- )
738
- block_y += block_size + 2
739
-
740
- identifiers.sort.each do |identifier, description|
741
- puts svg_extent_legend(
742
- graphic_x + block_x,
743
- graphic_y + block_y,
744
- block_size,
745
- "#" + RGBHEX_COLORS_RANDOM[(identifier * COLOR_SPACING_PRIME) % RGBHEX_COLORS_RANDOM.size],
746
- description,
747
- count_by_identifier[identifier],
748
- 100.0 * (count_by_identifier[identifier].to_f / space.pages.to_f)
749
- )
750
- block_y += block_size + 2
751
- end
752
-
753
- puts svg_extent_legend(
754
- graphic_x + block_x,
755
- graphic_y + block_y,
756
- block_size,
757
- "white",
758
- "Free space",
759
- count_by_identifier[:free],
760
- 100.0 * (count_by_identifier[:free].to_f / space.pages.to_f)
761
- )
762
-
763
- puts "</svg>"
764
- end
765
-
766
-
767
555
  def space_lsn_age_illustrate(space)
768
556
  colors = ANSI_COLORS_HEATMAP
769
- line_width = @options.illustration_line_width
557
+ width = @options.illustration_line_width
770
558
 
771
559
  # Calculate the minimum and maximum LSN in the space. This is pretty
772
560
  # inefficient as we end up scanning all pages twice.
@@ -774,70 +562,57 @@ def space_lsn_age_illustrate(space)
774
562
 
775
563
  lsn_min = lsn_max = space.page(0).lsn
776
564
  space.each_page do |page_number, page|
777
- if page.lsn != 0
778
- page_lsn[page_number] = page.lsn
779
- lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
780
- lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
781
- end
565
+ next if page.lsn.zero?
566
+
567
+ page_lsn[page_number] = page.lsn
568
+ lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
569
+ lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
782
570
  end
783
571
  lsn_delta = lsn_max - lsn_min
784
572
 
785
573
  puts
786
- puts "%12s ╭%-#{line_width}s" % [ "Start Page", "─" * line_width ]
574
+ puts "%12s %-#{width}s " % ["", center(space.name, width)]
575
+ puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
787
576
 
788
577
  start_page = 0
789
- page_lsn.each_slice(line_width) do |slice|
790
- puts "%12i │%-#{line_width}s│" % [
578
+ page_lsn.each_slice(width) do |slice|
579
+ puts "%12i │%-#{width}s│" % [
791
580
  start_page,
792
- slice.inject("") { |line, lsn|
581
+ slice.inject("") do |line, lsn|
793
582
  if lsn
794
- age_ratio = (lsn - lsn_min).to_f / lsn_delta.to_f
583
+ age_ratio = (lsn - lsn_min).to_f / lsn_delta
795
584
  color = colors[(age_ratio * colors.size.to_f).floor]
796
585
  line += ansi_color(color, filled_block(1.0, nil))
797
586
  else
798
587
  line += " "
799
588
  end
800
589
  line
801
- },
590
+ end,
802
591
  ]
803
- start_page += line_width
592
+ start_page += width
804
593
  end
805
594
 
806
- puts "%12s ╰%-#{line_width}s╯" % [ "", "─" * line_width ]
807
-
808
- lsn_legend = "<" + ("─" * (colors.size - 2)) + ">"
809
-
810
- begin
811
- # Try to optionally replace the boring lsn_legend with a histogram of
812
- # page age distribution. If histogram/array is not available, move on.
813
-
814
- require 'histogram/array'
815
-
816
- lsn_bins, lsn_freq = page_lsn.select { |lsn| !lsn.nil? }.
817
- histogram(colors.size, :min => lsn_min, :max => lsn_max)
595
+ puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
818
596
 
819
- lsn_freq_delta = lsn_freq.max - lsn_freq.min
597
+ _, lsn_freq = page_lsn.reject(&:nil?).histogram(colors.size, min: lsn_min, max: lsn_max)
598
+ lsn_freq_delta = lsn_freq.max - lsn_freq.min
820
599
 
821
- lsn_legend = ""
822
- lsn_freq.each do |freq|
823
- freq_norm = freq / lsn_freq_delta
824
- if freq_norm > 0.0
825
- lsn_legend << filled_block(freq_norm)
826
- else
827
- # Avoid the "empty" block used for 0.0.
828
- lsn_legend << " "
829
- end
830
- end
831
- rescue LoadError
832
- # That's okay! Leave the legend boring.
600
+ lsn_age_histogram = "".dup
601
+ lsn_freq.each do |freq|
602
+ freq_norm = freq / lsn_freq_delta
603
+ lsn_age_histogram << (freq_norm > 0.0 ? filled_block(freq_norm) : " ")
833
604
  end
834
605
 
835
606
  puts
836
- puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
607
+ puts "LSN Age Histogram (%s = ~%d pages):" % [
608
+ filled_block(1.0, nil),
609
+ (space.pages.to_f / colors.size).round,
610
+ ]
837
611
  puts " %12s %s %-12s" % [
838
612
  "Min LSN",
839
- lsn_legend,
840
- "Max LSN" ]
613
+ lsn_age_histogram,
614
+ "Max LSN",
615
+ ]
841
616
  puts " %12i %s %-12i" % [
842
617
  lsn_min,
843
618
  colors.map { |c| ansi_color(c, filled_block(1.0, nil)) }.join,
@@ -845,126 +620,6 @@ def space_lsn_age_illustrate(space)
845
620
  ]
846
621
  end
847
622
 
848
- def space_lsn_age_illustrate_svg(space)
849
- colors = RGBHEX_COLORS_HEATMAP
850
- line_width = @options.illustration_line_width
851
- block_size = @options.illustration_block_size
852
-
853
- puts "<?xml version=\"1.0\"?>"
854
- puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
855
-
856
- # Calculate the minimum and maximum LSN in the space. This is pretty
857
- # inefficient as we end up scanning all pages twice.
858
- page_lsn = Array.new(space.pages)
859
-
860
- lsn_min = lsn_max = space.page(0).lsn
861
- space.each_page do |page_number, page|
862
- if page.lsn != 0
863
- page_lsn[page_number] = page.lsn
864
- lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
865
- lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
866
- end
867
- end
868
- lsn_delta = lsn_max - lsn_min
869
-
870
- graphic_x = 48
871
- graphic_y = 16
872
-
873
- block_x = 0
874
- block_y = 0
875
-
876
- puts svg("text", {
877
- "y" => graphic_y - 3,
878
- "x" => graphic_x - 7,
879
- "font-family" => "monospace",
880
- "font-size" => block_size,
881
- "font-weight" => "bold",
882
- "text-anchor" => "end",
883
- }, "Page")
884
-
885
- start_page = 0
886
- page_lsn.each_slice(line_width) do |slice|
887
- block_x = 0
888
- slice.each do |lsn|
889
- rgbhex = ""
890
- if lsn
891
- age_ratio = (lsn - lsn_min).to_f / lsn_delta.to_f
892
- color = colors[(age_ratio * colors.size.to_f).floor]
893
- end
894
- puts svg("rect", {
895
- "y" => graphic_y + block_y,
896
- "x" => graphic_x + block_x,
897
- "width" => block_size,
898
- "height" => block_size,
899
- "fill" => color ? "#" + color : "black",
900
- })
901
- block_x += block_size
902
- end
903
- puts svg("text", {
904
- "y" => graphic_y + block_y + block_size,
905
- "x" => graphic_x - 7,
906
- "font-family" => "monospace",
907
- "font-size" => block_size,
908
- "text-anchor" => "end",
909
- }, start_page)
910
- block_y += block_size
911
- start_page += line_width
912
- end
913
-
914
- puts svg("path", {
915
- "stroke" => "black",
916
- "stroke-width" => 1,
917
- "fill" => "none",
918
- "d" => svg_path_rounded_rect(
919
- graphic_x,
920
- graphic_y,
921
- line_width * block_size,
922
- block_y,
923
- 4
924
- ),
925
- })
926
-
927
- block_x = 0
928
- block_y += 16
929
- puts svg("text", {
930
- "y" => graphic_y + block_y + block_size - 4,
931
- "x" => graphic_x + block_x,
932
- "font-family" => "monospace",
933
- "font-size" => block_size,
934
- "text-anchor" => "start",
935
- }, lsn_min)
936
- color_width = ((64.0 * block_size.to_f) / colors.size.to_f).round
937
- colors.each do |color|
938
- puts svg("rect", {
939
- "y" => graphic_y + block_y + block_size,
940
- "x" => graphic_x + block_x,
941
- "width" => color_width,
942
- "height" => block_size,
943
- "fill" => "#" + color,
944
- })
945
- block_x += color_width
946
- end
947
- puts svg("text", {
948
- "y" => graphic_y + block_y + block_size - 4,
949
- "x" => graphic_x + block_x,
950
- "font-family" => "monospace",
951
- "font-size" => block_size,
952
- "text-anchor" => "end",
953
- }, lsn_max)
954
-
955
- puts svg("text", {
956
- "y" => graphic_y + block_y + block_size - 4,
957
- "x" => graphic_x + (block_x / 2),
958
- "font-family" => "monospace",
959
- "font-weight" => "bold",
960
- "font-size" => block_size,
961
- "text-anchor" => "middle",
962
- }, "LSN Age")
963
-
964
-
965
- puts "</svg>\n"
966
- end
967
-
968
623
  def print_inode_summary(inode)
969
624
  puts "INODE fseg_id=%d, pages=%d, frag=%d, full=%d, not_full=%d, free=%d" % [
970
625
  inode.fseg_id,
@@ -977,6 +632,7 @@ def print_inode_summary(inode)
977
632
  end
978
633
 
979
634
  def print_inode_detail(inode)
635
+ # rubocop:disable Layout/LineLength
980
636
  puts "INODE fseg_id=%d, pages=%d, frag=%d pages (%s), full=%d extents (%s), not_full=%d extents (%s) (%d/%d pages used), free=%d extents (%s)" % [
981
637
  inode.fseg_id,
982
638
  inode.total_pages,
@@ -991,6 +647,7 @@ def print_inode_detail(inode)
991
647
  inode.free.length,
992
648
  inode.free.each.to_a.map { |x| "#{x.start_page}-#{x.end_page}" }.join(", "),
993
649
  ]
650
+ # rubocop:enable Layout/LineLength
994
651
  end
995
652
 
996
653
  def space_inodes_fseg_id(space)
@@ -1027,7 +684,6 @@ def page_account(innodb_system, space, page_number)
1027
684
  page_type[:usage],
1028
685
  ]
1029
686
 
1030
-
1031
687
  xdes = space.xdes_for_page(page_number)
1032
688
  puts " Extent descriptor for pages %d-%d is at page %d, offset %d." % [
1033
689
  xdes.start_page,
@@ -1044,25 +700,23 @@ def page_account(innodb_system, space, page_number)
1044
700
 
1045
701
  xdes_status = xdes.page_status(page_number)
1046
702
  puts " Page is marked as %s in extent descriptor." % [
1047
- xdes_status[:free] ? 'free' : 'used'
703
+ xdes_status[:free] ? "free" : "used",
1048
704
  ]
1049
705
 
1050
706
  space.each_xdes_list do |name, list|
1051
- if list.include? xdes
1052
- puts " Extent is in #{name} list of space."
1053
- end
707
+ puts " Extent is in #{name} list of space." if list.include?(xdes)
1054
708
  end
1055
709
 
1056
710
  page_inode = nil
1057
711
  space.each_inode do |inode|
1058
712
  inode.each_list do |name, list|
1059
- if list.include? xdes
713
+ if list.include?(xdes)
1060
714
  page_inode = inode
1061
715
  puts " Extent is in #{name} list of fseg #{inode.fseg_id}."
1062
716
  end
1063
717
  end
1064
718
 
1065
- if inode.frag_array.include? page_number
719
+ if inode.frag_array.include?(page_number) # rubocop:disable Style/Next
1066
720
  page_inode = inode
1067
721
  puts " Page is in fragment array of fseg %d." % [
1068
722
  inode.fseg_id,
@@ -1072,42 +726,30 @@ def page_account(innodb_system, space, page_number)
1072
726
 
1073
727
  space.each_index do |index|
1074
728
  index.each_fseg do |fseg_name, fseg|
1075
- if page_inode == fseg
1076
- puts " Fseg is in #{fseg_name} fseg of index #{index.id}."
1077
- puts " Index root is page #{index.root.offset}."
1078
- if innodb_system
1079
- table_name, index_name = innodb_system.table_and_index_name_by_id(index.id)
1080
- if table_name and index_name
1081
- puts " Index is #{table_name}.#{index_name}."
1082
- end
1083
- end
729
+ next unless page_inode == fseg
730
+
731
+ puts " Fseg is in #{fseg_name} fseg of index #{index.id}."
732
+ puts " Index root is page #{index.root.offset}."
733
+ if innodb_system
734
+ table_name, index_name = innodb_system.table_and_index_name_by_id(index.id)
735
+ puts " Index is #{table_name}.#{index_name}." if table_name && index_name
1084
736
  end
1085
737
  end
1086
738
  end
1087
739
 
1088
- if space.system_space?
1089
- if page_inode == space.trx_sys.fseg
1090
- puts " Fseg is trx_sys."
1091
- end
740
+ if space.system_space? # rubocop:disable Style/GuardClause
741
+ puts " Fseg is trx_sys." if page_inode == space.trx_sys.fseg
742
+ puts " Fseg is doublewrite buffer." if page_inode == space.trx_sys.doublewrite[:fseg]
1092
743
 
1093
- if page_inode == space.trx_sys.doublewrite[:fseg]
1094
- puts " Fseg is doublewrite buffer."
1095
- end
1096
-
1097
- if innodb_system
1098
- innodb_system.data_dictionary.each_data_dictionary_index do |table_name, index_name, index|
1099
- index.each_fseg do |fseg_name, fseg|
1100
- if page_inode == fseg
1101
- puts " Index is #{table_name}.#{index_name} of data dictionary."
1102
- end
1103
- end
744
+ innodb_system.data_dictionary&.each_data_dictionary_index do |table_name, index_name, index|
745
+ index.each_fseg do |_fseg_name, fseg|
746
+ puts " Index is #{table_name}.#{index_name} of data dictionary." if page_inode == fseg
1104
747
  end
1105
748
  end
1106
749
 
1107
750
  space.trx_sys.rsegs.each_with_index do |rseg_slot, index|
1108
- if page.fil_header[:space_id] == rseg_slot[:space_id] &&
1109
- page.fil_header[:offset] == rseg_slot[:page_number]
1110
- puts " Page is a rollback segment in slot #{index}."
751
+ if page.fil_header.space_id == rseg_slot.space_id && page.fil_header.offset == rseg_slot.page_number
752
+ puts " Page is a rollback segment in slot #{index}."
1111
753
  end
1112
754
  end
1113
755
  end
@@ -1121,10 +763,9 @@ def page_validate_index(page)
1121
763
  puts "done."
1122
764
 
1123
765
  directory_offsets = page.each_directory_offset.to_a
1124
- record_offsets = records.map { |rec| rec.offset }
766
+ record_offsets = records.map(&:offset)
1125
767
 
1126
- invalid_directory_entries =
1127
- directory_offsets.select { |n| ! record_offsets.include?(n) }
768
+ invalid_directory_entries = directory_offsets.reject { |n| record_offsets.include?(n) }
1128
769
 
1129
770
  unless invalid_directory_entries.empty?
1130
771
  page_is_valid = false
@@ -1138,9 +779,7 @@ def page_validate_index(page)
1138
779
  end
1139
780
 
1140
781
  # Read all records corresponding to valid directory entries.
1141
- directory_records = directory_offsets.
1142
- select { |o| !invalid_directory_entries.include?(o) }.
1143
- map { |o| page.record(o) }
782
+ directory_records = directory_offsets.reject { |o| invalid_directory_entries.include?(o) }.map { |o| page.record(o) }
1144
783
 
1145
784
  misordered_directory_entries = []
1146
785
  prev = nil
@@ -1149,13 +788,13 @@ def page_validate_index(page)
1149
788
  prev = rec
1150
789
  next
1151
790
  end
1152
- if rec.compare_key(prev.key.map { |v| v[:value]}) == 1
791
+ if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
1153
792
  page_is_valid = false
1154
793
  misordered_directory_entries << {
1155
- :slot => page.offset_is_directory_slot?(rec.offset),
1156
- :offset => rec.offset,
1157
- :key => rec.key_string,
1158
- :prev_key => prev.key_string,
794
+ slot: page.offset_is_directory_slot?(rec.offset),
795
+ offset: rec.offset,
796
+ key: rec.key_string,
797
+ prev_key: prev.key_string,
1159
798
  }
1160
799
  end
1161
800
  prev = rec
@@ -1179,12 +818,12 @@ def page_validate_index(page)
1179
818
  prev = rec
1180
819
  next
1181
820
  end
1182
- if rec.compare_key(prev.key.map { |v| v[:value]}) == 1
821
+ if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
1183
822
  page_is_valid = false
1184
823
  misordered_records << {
1185
- :offset => rec.offset,
1186
- :key => rec.key_string,
1187
- :prev_key => prev.key_string,
824
+ offset: rec.offset,
825
+ key: rec.key_string,
826
+ prev_key: prev.key_string,
1188
827
  }
1189
828
  end
1190
829
  prev = rec
@@ -1203,8 +842,7 @@ def page_validate_index(page)
1203
842
  page_is_valid
1204
843
  end
1205
844
 
1206
-
1207
- def page_validate(innodb_system, space, page_number)
845
+ def page_validate(_innodb_system, space, page_number)
1208
846
  page_is_valid = true
1209
847
  puts "Validating page %d..." % [page_number]
1210
848
 
@@ -1219,11 +857,11 @@ def page_validate(innodb_system, space, page_number)
1219
857
  puts " header %10d (0x%08x), type %s" % [
1220
858
  page.checksum,
1221
859
  page.checksum,
1222
- page.checksum_type ? page.checksum_type : "unknown",
860
+ page.checksum_type || "unknown",
1223
861
  ]
1224
862
  puts " trailer %10d (0x%08x)" % [
1225
- page.checksum_trailer,
1226
- page.checksum_trailer,
863
+ page.fil_trailer.checksum,
864
+ page.fil_trailer.checksum,
1227
865
  ]
1228
866
  puts " Calculated checksums:"
1229
867
  puts " crc32 %10d (0x%08x)" % [
@@ -1246,12 +884,12 @@ def page_validate(innodb_system, space, page_number)
1246
884
  ]
1247
885
  puts " Low 32 bits of LSN:"
1248
886
  puts " header %10d (0x%08x)" % [
1249
- page.lsn_low32_header,
1250
- page.lsn_low32_header,
887
+ page.fil_header.lsn_low32,
888
+ page.fil_header.lsn_low32,
1251
889
  ]
1252
890
  puts " trailer %10d (0x%08x)" % [
1253
- page.lsn_low32_trailer,
1254
- page.lsn_low32_trailer,
891
+ page.fil_trailer.lsn_low32,
892
+ page.fil_trailer.lsn_low32,
1255
893
  ]
1256
894
  end
1257
895
 
@@ -1265,16 +903,14 @@ def page_validate(innodb_system, space, page_number)
1265
903
  ]
1266
904
  end
1267
905
  if page.misplaced_space?
1268
- puts " Space's ID %d does not match page's stored space ID %d." % [
906
+ puts " Space ID %d does not match page stored space ID %d." % [
1269
907
  page.space.space_id,
1270
908
  page.space_id,
1271
909
  ]
1272
910
  end
1273
911
  end
1274
912
 
1275
- if page.type == :INDEX && !page_validate_index(page)
1276
- page_is_valid = false
1277
- end
913
+ page_is_valid = false if page.type == :INDEX && !page_validate_index(page)
1278
914
 
1279
915
  puts "Page %d appears to be %s!" % [
1280
916
  page_number,
@@ -1282,24 +918,21 @@ def page_validate(innodb_system, space, page_number)
1282
918
  ]
1283
919
  end
1284
920
 
1285
- def page_directory_summary(space, page)
1286
- if page.type != :INDEX
1287
- usage 1, "Page must be an index page"
1288
- end
921
+ def page_directory_summary(_space, page)
922
+ usage(1, "Page must be an index page") if page.type != :INDEX
1289
923
 
1290
- puts "%-8s%-8s%-14s%-8s%s" % [
1291
- "slot",
1292
- "offset",
1293
- "type",
1294
- "owned",
1295
- "key",
924
+ puts "%-8s%-8s%-14s%-8s%s" % %w[
925
+ slot
926
+ offset
927
+ type
928
+ owned
929
+ key
1296
930
  ]
1297
931
 
1298
932
  page.directory.each_with_index do |offset, slot|
1299
933
  record = page.record(offset)
1300
- key = if [:conventional, :node_pointer].include? record.header[:type]
1301
- "(%s)" % record.key_string
1302
- end
934
+ key = %i[conventional node_pointer].include?(record.header[:type]) ? "(%s)" % record.key_string : ""
935
+
1303
936
  puts "%-8i%-8i%-14s%-8i%s" % [
1304
937
  slot,
1305
938
  offset,
@@ -1310,13 +943,12 @@ def page_directory_summary(space, page)
1310
943
  end
1311
944
  end
1312
945
 
1313
- def page_records(space, page)
946
+ def page_records(_space, page)
1314
947
  page.each_record do |record|
1315
948
  puts "Record %i: %s" % [
1316
949
  record.offset,
1317
950
  record.string,
1318
951
  ]
1319
- puts if record.header[:type] == :conventional
1320
952
  end
1321
953
  end
1322
954
 
@@ -1328,40 +960,46 @@ def page_illustrate(page)
1328
960
  identifier_sort = 0
1329
961
  count_by_identifier = Hash.new(0)
1330
962
 
1331
- page.each_region.sort { |a,b| a[:offset] <=> b[:offset]}.each do |region|
1332
- region[:length].times do |n|
963
+ page.each_region.sort_by(&:offset).each do |region|
964
+ region.length.times do |n|
1333
965
  identifier = nil
1334
966
  fraction = 0.0
1335
- if region[:name] != :garbage
1336
- if n == region[:length] - 1
1337
- fraction = 0.5
1338
- else
1339
- fraction = 1.0
1340
- end
1341
- identifier = region[:name].hash.abs
967
+ if region.name != :garbage
968
+ fraction = n == region.length - 1 ? 0.5 : 1.0
969
+ identifier = region.name.hash.abs
1342
970
  unless identifiers[identifier]
1343
971
  # Prefix an integer <0123> on each name so that the legend can be
1344
972
  # sorted by the appearance of each region in the page.
1345
973
  identifiers[identifier] = "<%04i>%s" % [
1346
974
  identifier_sort,
1347
- region[:info]
975
+ region.info,
1348
976
  ]
1349
977
  identifier_sort += 1
1350
978
  end
1351
979
  end
1352
- blocks[region[:offset] + n] = filled_block(fraction, identifier, BLOCK_CHARS_H)
980
+ blocks[region.offset + n] = filled_block(fraction, identifier, BLOCK_CHARS_H)
1353
981
  count_by_identifier[identifier] += 1
1354
982
  end
1355
983
  end
1356
984
 
1357
985
  puts
1358
- puts "%12s ╭%-#{width}s" % [ "Offset", "" * width ]
986
+ puts "%12s %-#{width}s " % ["", center("Page #{page.offset} (#{page.type})", width)]
987
+ puts "%12s ╭%-#{width}s╮" % ["Offset", "─" * width]
1359
988
  offset = 0
989
+ skipped_lines = 0
1360
990
  blocks.each_slice(width) do |slice|
1361
- puts "%12i │%-s" % [offset, slice.join]
991
+ if slice.any? { |s| s != " " }
992
+ if skipped_lines.positive?
993
+ puts "%12s │%-#{width}s│" % ["...", ""]
994
+ skipped_lines = 0
995
+ end
996
+ puts "%12i │%-s│" % [offset, slice.join]
997
+ else
998
+ skipped_lines += 1
999
+ end
1362
1000
  offset += width
1363
1001
  end
1364
- puts "%12s ╰%-#{width}s╯" % [ "", "─" * width ]
1002
+ puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
1365
1003
 
1366
1004
  puts
1367
1005
  puts "Legend (%s = 1 byte):" % [filled_block(1.0, nil)]
@@ -1370,26 +1008,26 @@ def page_illustrate(page)
1370
1008
  "Bytes",
1371
1009
  "Ratio",
1372
1010
  ]
1373
- identifiers.sort { |a,b| a[1] <=> b[1] }.each do |identifier, description|
1011
+ identifiers.sort { |a, b| a[1] <=> b[1] }.each do |identifier, description|
1374
1012
  puts " %s %-30s %8i %7.2f%%" % [
1375
1013
  filled_block(1.0, identifier),
1376
1014
  description.gsub(/^<\d+>/, ""),
1377
1015
  count_by_identifier[identifier],
1378
- 100.0 * (count_by_identifier[identifier].to_f / page.size.to_f),
1016
+ 100.0 * (count_by_identifier[identifier].to_f / page.size),
1379
1017
  ]
1380
1018
  end
1381
1019
  puts " %s %-30s %8i %7.2f%%" % [
1382
1020
  filled_block(0.0, nil),
1383
1021
  "Garbage",
1384
1022
  count_by_identifier[nil],
1385
- 100.0 * (count_by_identifier[nil].to_f / page.size.to_f),
1023
+ 100.0 * (count_by_identifier[nil].to_f / page.size),
1386
1024
  ]
1387
- free_space = page.size - count_by_identifier.inject(0) { |sum,(k,v)| sum + v }
1025
+ free_space = page.size - count_by_identifier.inject(0) { |sum, (_k, v)| sum + v }
1388
1026
  puts " %s %-30s %8i %7.2f%%" % [
1389
1027
  unknown_page_content ? "▞" : " ",
1390
1028
  unknown_page_content ? "Unknown (no data dictionary)" : "Free",
1391
1029
  free_space,
1392
- 100.0 * (free_space.to_f / page.size.to_f),
1030
+ 100.0 * (free_space.to_f / page.size),
1393
1031
  ]
1394
1032
 
1395
1033
  if unknown_page_content
@@ -1403,21 +1041,17 @@ def page_illustrate(page)
1403
1041
  end
1404
1042
 
1405
1043
  def record_dump(page, record_offset)
1406
- unless record = page.record(record_offset)
1407
- raise "Record at offset #{record_offset} not found"
1408
- end
1044
+ record = page.record(record_offset)
1045
+ raise "Record at offset #{record_offset} not found" unless record
1409
1046
 
1410
1047
  record.dump
1411
1048
  end
1412
1049
 
1413
1050
  def record_history(page, record_offset)
1414
- unless page.leaf?
1415
- raise "Record is not located on a leaf page; no history available"
1416
- end
1051
+ raise "Record is not located on a leaf page; no history available" unless page.leaf?
1417
1052
 
1418
- unless record = page.record(record_offset)
1419
- raise "Record at offset #{record_offset} not found"
1420
- end
1053
+ record = page.record(record_offset)
1054
+ raise "Record at offset #{record_offset} not found" unless record
1421
1055
 
1422
1056
  puts "%-14s%-20s%s" % [
1423
1057
  "Transaction",
@@ -1435,29 +1069,23 @@ def record_history(page, record_offset)
1435
1069
  end
1436
1070
 
1437
1071
  def index_fseg_lists(index, fseg_name)
1438
- unless index.fseg(fseg_name)
1439
- raise "File segment '#{fseg_name}' doesn't exist"
1440
- end
1072
+ raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
1441
1073
 
1442
1074
  print_lists(index.each_fseg_list(index.fseg(fseg_name)))
1443
1075
  end
1444
1076
 
1445
1077
  def index_fseg_list_iterate(index, fseg_name, list_name)
1446
- unless fseg = index.fseg(fseg_name)
1447
- raise "File segment '#{fseg_name}' doesn't exist"
1448
- end
1078
+ fseg = index.fseg(fseg_name)
1079
+ raise "File segment '#{fseg_name}' doesn't exist" unless fseg
1449
1080
 
1450
- unless list = fseg.list(list_name)
1451
- raise "List '#{list_name}' doesn't exist"
1452
- end
1081
+ list = fseg.list(list_name)
1082
+ raise "List '#{list_name}' doesn't exist" unless list
1453
1083
 
1454
1084
  print_xdes_list(index.space, list)
1455
1085
  end
1456
1086
 
1457
1087
  def index_fseg_frag_pages(index, fseg_name)
1458
- unless index.fseg(fseg_name)
1459
- raise "File segment '#{fseg_name}' doesn't exist"
1460
- end
1088
+ raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
1461
1089
 
1462
1090
  print_index_page_summary(index.each_fseg_frag_page(index.fseg(fseg_name)))
1463
1091
  end
@@ -1472,17 +1100,17 @@ def index_recurse(index)
1472
1100
  page.records,
1473
1101
  page.record_space,
1474
1102
  ]
1475
- if page.level == 0
1103
+ if page.level.zero?
1476
1104
  page.each_record do |record|
1477
1105
  puts "%sRECORD: (%s) → (%s)" % [
1478
- " " * (depth+1),
1106
+ " " * (depth + 1),
1479
1107
  record.key_string,
1480
1108
  record.row_string,
1481
1109
  ]
1482
1110
  end
1483
1111
  end
1484
1112
  end,
1485
- lambda do |parent_page, child_page, child_min_key, depth|
1113
+ lambda do |_parent_page, child_page, child_min_key, depth|
1486
1114
  puts "%sNODE POINTER RECORD ≥ (%s) → #%i" % [
1487
1115
  " " * depth,
1488
1116
  child_min_key.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }.join(", "),
@@ -1493,13 +1121,13 @@ def index_recurse(index)
1493
1121
  end
1494
1122
 
1495
1123
  def index_record_offsets(index)
1496
- puts "%-20s%-20s" % [
1497
- "page_offset",
1498
- "record_offset",
1124
+ puts "%-20s%-20s" % %w[
1125
+ page_offset
1126
+ record_offset
1499
1127
  ]
1500
1128
  index.recurse(
1501
- lambda do |page, depth|
1502
- if page.level == 0
1129
+ lambda do |page, _depth|
1130
+ if page.level.zero?
1503
1131
  page.each_record do |record|
1504
1132
  puts "%-20i%-20i" % [
1505
1133
  page.offset,
@@ -1508,7 +1136,7 @@ def index_record_offsets(index)
1508
1136
  end
1509
1137
  end
1510
1138
  end,
1511
- lambda { |*x| }
1139
+ ->(*_) {}
1512
1140
  )
1513
1141
  end
1514
1142
 
@@ -1528,13 +1156,13 @@ def index_digraph(index)
1528
1156
  child_key.join(", "),
1529
1157
  ]
1530
1158
  end
1531
- puts " %spage_%i [ shape = \"record\"; label = \"%s\"; ];" % [
1159
+ puts " %spage_%i [ shape = 'record'; label = '%s'; ];" % [
1532
1160
  " " * depth,
1533
1161
  page.offset,
1534
1162
  label,
1535
1163
  ]
1536
1164
  end,
1537
- lambda do |parent_page, child_page, child_key, depth|
1165
+ lambda do |parent_page, child_page, _child_key, depth|
1538
1166
  puts " %spage_%i:dir_%i → page_%i:page:nw;" % [
1539
1167
  " " * depth,
1540
1168
  parent_page.offset,
@@ -1547,14 +1175,14 @@ def index_digraph(index)
1547
1175
  end
1548
1176
 
1549
1177
  def index_level_summary(index, level)
1550
- puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % [
1551
- "page",
1552
- "index",
1553
- "level",
1554
- "data",
1555
- "free",
1556
- "records",
1557
- "min_key",
1178
+ puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % %w[
1179
+ page
1180
+ index
1181
+ level
1182
+ data
1183
+ free
1184
+ records
1185
+ min_key
1558
1186
  ]
1559
1187
 
1560
1188
  index.each_page_at_level(level) do |page|
@@ -1571,15 +1199,14 @@ def index_level_summary(index, level)
1571
1199
  end
1572
1200
 
1573
1201
  def undo_history_summary(innodb_system)
1574
- history = innodb_system.history.each_history_list
1575
- history_list = history.select { |history| history.list.length > 0 }
1576
-
1577
- puts "%-8s%-8s%-14s%-20s%s" % [
1578
- "Page",
1579
- "Offset",
1580
- "Transaction",
1581
- "Type",
1582
- "Table",
1202
+ history_list = innodb_system.history.each_history_list.reject { |h| h.list.empty? }
1203
+
1204
+ puts "%-8s%-8s%-14s%-20s%s" % %w[
1205
+ Page
1206
+ Offset
1207
+ Transaction
1208
+ Type
1209
+ Table
1583
1210
  ]
1584
1211
 
1585
1212
  history_list.each do |history|
@@ -1609,6 +1236,7 @@ def usage(exit_code, message = nil)
1609
1236
  exit exit_code
1610
1237
  end
1611
1238
 
1239
+ # rubocop:disable Layout/HeredocIndentation
1612
1240
  print <<'END_OF_USAGE'
1613
1241
 
1614
1242
  Usage: innodb_space <options> <mode>
@@ -1636,9 +1264,9 @@ The following options are supported:
1636
1264
 
1637
1265
  --system-space-file, -s <arg>
1638
1266
  Load the system tablespace file or files <arg>: Either a single file e.g.
1639
- "ibdata1", a comma-delimited list of files e.g. "ibdata1,ibdata1", or a
1267
+ 'ibdata1', a comma-delimited list of files e.g. 'ibdata1,ibdata1', or a
1640
1268
  directory name. If a directory name is provided, it will be scanned for all
1641
- files named "ibdata?" which will then be sorted alphabetically and used to
1269
+ files named 'ibdata?' which will then be sorted alphabetically and used to
1642
1270
  load the system tablespace.
1643
1271
 
1644
1272
  --table-name, -T <name>
@@ -1663,7 +1291,7 @@ The following options are supported:
1663
1291
  Operate on the file segment (fseg) <fseg_id>.
1664
1292
 
1665
1293
  --require, -r <file>
1666
- Use Ruby's "require" to load the file <file>. This is useful for loading
1294
+ Use Ruby's 'require' to load the file <file>. This is useful for loading
1667
1295
  classes with record describers.
1668
1296
 
1669
1297
  --describer, -d <describer>
@@ -1691,9 +1319,9 @@ The following modes are supported:
1691
1319
  provided with the --page/-p argument.
1692
1320
 
1693
1321
  space-index-pages-summary
1694
- Summarize all "INDEX" pages within a tablespace. This is useful to analyze
1695
- page fill rates and record counts per page. In addition to "INDEX" pages,
1696
- "ALLOCATED" pages are also printed and assumed to be completely empty.
1322
+ Summarize all 'INDEX' pages within a tablespace. This is useful to analyze
1323
+ page fill rates and record counts per page. In addition to 'INDEX' pages,
1324
+ 'ALLOCATED' pages are also printed and assumed to be completely empty.
1697
1325
  A starting page number can be provided with the --page/-p argument.
1698
1326
 
1699
1327
  space-index-fseg-pages-summary
@@ -1702,7 +1330,7 @@ The following modes are supported:
1702
1330
 
1703
1331
  space-index-pages-free-plot
1704
1332
  Use Ruby's gnuplot module to produce a scatterplot of page free space for
1705
- all "INDEX" and "ALLOCATED" pages in a tablespace. More aesthetically
1333
+ all 'INDEX' and 'ALLOCATED' pages in a tablespace. More aesthetically
1706
1334
  pleasing plots can be produced with space-index-pages-summary output,
1707
1335
  but this is a quick and easy way to produce a passable plot. A starting
1708
1336
  page number can be provided with the --page/-p argument.
@@ -1734,20 +1362,11 @@ The following modes are supported:
1734
1362
  color and Unicode box drawing characters to show page usage throughout
1735
1363
  the space.
1736
1364
 
1737
- space-extents-illustrate-svg
1738
- Iterate through all extents, illustrating the extent usage in SVG format
1739
- printed to stdout to show page usage throughout the space.
1740
-
1741
1365
  space-lsn-age-illustrate
1742
1366
  Iterate through all pages, producing a heat map colored by the page LSN
1743
1367
  using ANSI color and Unicode box drawing characters, allowing the user to
1744
1368
  get an overview of page modification recency.
1745
1369
 
1746
- space-lsn-age-illustrate-svg
1747
- Iterate through all pages, producing a heat map colored by the page LSN
1748
- producing SVG format output, allowing the user to get an overview of page
1749
- modification recency.
1750
-
1751
1370
  space-inodes-fseg-id
1752
1371
  Iterate through all inodes, printing only the FSEG ID.
1753
1372
 
@@ -1785,7 +1404,7 @@ The following modes are supported:
1785
1404
  Iterate the file segment list (whose name is provided in the first --list/-L
1786
1405
  argument) for internal or leaf pages for a given index (whose root page
1787
1406
  is provided in the first --page/-p argument). The lists used for each
1788
- index are "full", "not_full", and "free".
1407
+ index are 'full', 'not_full', and 'free'.
1789
1408
 
1790
1409
  index-fseg-internal-frag-pages
1791
1410
  index-fseg-leaf-frag-pages
@@ -1793,7 +1412,7 @@ The following modes are supported:
1793
1412
  page must be provided with --page/-p.
1794
1413
 
1795
1414
  page-dump
1796
- Dump the contents of a page, using the Ruby pp ("pretty-print") module.
1415
+ Dump the contents of a page, using the Ruby pp ('pretty-print') module.
1797
1416
 
1798
1417
  page-account
1799
1418
  Account for a page's usage in FSEGs.
@@ -1827,6 +1446,7 @@ The following modes are supported:
1827
1446
  A record offset must be provided with -R/--record.
1828
1447
 
1829
1448
  END_OF_USAGE
1449
+ # rubocop:enable Layout/HeredocIndentation
1830
1450
 
1831
1451
  exit exit_code
1832
1452
  end
@@ -1849,6 +1469,7 @@ Signal.trap("PIPE") { exit }
1849
1469
  @options.illustration_line_width = 64
1850
1470
  @options.illustration_block_size = 8
1851
1471
 
1472
+ # rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
1852
1473
  getopt_options = [
1853
1474
  [ "--help", "-?", GetoptLong::NO_ARGUMENT ],
1854
1475
  [ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
@@ -1866,6 +1487,7 @@ getopt_options = [
1866
1487
  [ "--illustration-line-width", GetoptLong::REQUIRED_ARGUMENT ],
1867
1488
  [ "--illustration-block-size", GetoptLong::REQUIRED_ARGUMENT ],
1868
1489
  ]
1490
+ # rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
1869
1491
 
1870
1492
  getopt = GetoptLong.new(*getopt_options)
1871
1493
 
@@ -1904,17 +1526,17 @@ getopt.each do |opt, arg|
1904
1526
  end
1905
1527
  end
1906
1528
 
1907
- unless @options.system_space_file or @options.space_file
1529
+ # rubocop:disable Style/IfUnlessModifier
1530
+
1531
+ unless @options.system_space_file || @options.space_file
1908
1532
  usage 1, "System space file (-s) or space file (-f) must be specified"
1909
1533
  end
1910
1534
 
1911
- if @options.system_space_file and @options.space_file
1535
+ if @options.system_space_file && @options.space_file
1912
1536
  usage 1, "Only one of system space or space file may be specified"
1913
1537
  end
1914
1538
 
1915
- if @options.trace > 1
1916
- BufferCursor.trace!
1917
- end
1539
+ BufferCursor.trace! if @options.trace > 1
1918
1540
 
1919
1541
  # A few globals that we'll try to populate from the command-line arguments.
1920
1542
  innodb_system = nil
@@ -1926,7 +1548,7 @@ if @options.system_space_file
1926
1548
  innodb_system = Innodb::System.new(@options.system_space_file)
1927
1549
  end
1928
1550
 
1929
- if innodb_system and @options.table_name
1551
+ if innodb_system && @options.table_name
1930
1552
  table_tablespace = innodb_system.space_by_table_name(@options.table_name)
1931
1553
  space = table_tablespace || innodb_system.system_space
1932
1554
  elsif @options.space_file
@@ -1936,24 +1558,17 @@ else
1936
1558
  end
1937
1559
 
1938
1560
  if @options.describer
1939
- describer = eval(@options.describer)
1940
- unless describer
1941
- describer = Innodb::RecordDescriber.const_get(@options.describer)
1942
- end
1561
+ describer = eval(@options.describer) # rubocop:disable Security/Eval
1562
+ describer ||= Innodb::RecordDescriber.const_get(@options.describer)
1943
1563
  space.record_describer = describer.new
1944
1564
  end
1945
1565
 
1946
- if innodb_system and @options.table_name and @options.index_name
1566
+ if innodb_system && @options.table_name && @options.index_name
1947
1567
  index = innodb_system.index_by_name(@options.table_name, @options.index_name)
1948
- if @options.page
1949
- page = space.page(@options.page)
1950
- else
1951
- page = index.root
1952
- end
1568
+ page = @options.page ? space.page(@options.page) : index.root
1953
1569
  elsif @options.page
1954
- if page = space.page(@options.page) and page.type == :INDEX and page.root?
1955
- index = space.index(@options.page)
1956
- end
1570
+ page = space.page(@options.page)
1571
+ index = space.index(@options.page) if page&.type == :INDEX && page&.root?
1957
1572
  end
1958
1573
 
1959
1574
  # The non-option argument on the command line is the mode (usually the last,
@@ -1961,53 +1576,63 @@ end
1961
1576
  mode = ARGV.shift
1962
1577
 
1963
1578
  unless mode
1964
- usage 1, "At least one mode must be provided"
1579
+ usage(1, "At least one mode must be provided")
1965
1580
  end
1966
1581
 
1967
- if /^(system-|data-dictionary-)/.match(mode) and !innodb_system
1968
- usage 1, "System tablespace must be specified using -s/--system-space-file"
1582
+ if /^(system-|data-dictionary-)/.match(mode) && !innodb_system
1583
+ usage(1, "System tablespace must be specified using -s/--system-space-file")
1969
1584
  end
1970
1585
 
1971
- if /^space-/.match(mode) and !space
1972
- usage 1, "Tablespace must be specified using either -f/--space-file or a combination of -s/--system-space-file and -T/--table"
1586
+ if /^space-/.match(mode) && !space
1587
+ usage(
1588
+ 1,
1589
+ "Tablespace must be specified using either -f/--space-file " \
1590
+ "or a combination of -s/--system-space-file and -T/--table"
1591
+ )
1973
1592
  end
1974
1593
 
1975
- if /^index-/.match(mode) and !index
1976
- usage 1, "Index must be specified using a combination of either -f/--space-file and -p/--page or -s/--system-space-file, -T/--table-name, and -I/--index-name"
1594
+ if /^index-/.match(mode) && !index
1595
+ usage(
1596
+ 1,
1597
+ "Index must be specified using a combination of either -f/--space-file and -p/--page " \
1598
+ "or -s/--system-space-file, -T/--table-name, and -I/--index-name"
1599
+ )
1977
1600
  end
1978
1601
 
1979
- if /^page-/.match(mode) and !page
1980
- usage 1, "Page number must be specified using -p/--page"
1602
+ if /^page-/.match(mode) && !page
1603
+ usage(1, "Page number must be specified using -p/--page")
1981
1604
  end
1982
1605
 
1983
- if /^record-/.match(mode) and !@options.record
1984
- usage 1, "Record offset must be specified using -R/--record"
1606
+ if /^record-/.match(mode) && !@options.record
1607
+ usage(1, "Record offset must be specified using -R/--record")
1985
1608
  end
1986
1609
 
1987
- if /-list-iterate$/.match(mode) and !@options.list
1988
- usage 1, "List name must be specified using -L/--list"
1610
+ if /-list-iterate$/.match(mode) && !@options.list
1611
+ usage(1, "List name must be specified using -L/--list")
1989
1612
  end
1990
1613
 
1991
- if /-level-/.match(mode) and !@options.level
1992
- usage 1, "Level must be specified using -l/--level"
1614
+ if /-level-/.match(mode) && !@options.level
1615
+ usage(1, "Level must be specified using -l/--level")
1993
1616
  end
1994
1617
 
1995
- if [
1996
- "index-recurse",
1997
- "index-record-offsets",
1998
- "index-digraph",
1999
- "index-level-summary",
2000
- ].include?(mode) and !index.record_describer
2001
- usage 1, "Record describer must be specified using -d/--describer"
1618
+ if %w[
1619
+ index-recurse
1620
+ index-record-offsets
1621
+ index-digraph
1622
+ index-level-summary
1623
+ ].include?(mode) && !index.record_describer
1624
+ usage(1, "Record describer must be specified using -d/--describer")
2002
1625
  end
2003
1626
 
2004
- if ["space-index-fseg-pages-summary"].include?(mode) and !@options.fseg_id
2005
- usage 1, "File segment id must be specified using -F/--fseg-id"
1627
+ if %w[
1628
+ space-index-fseg-pages-summary
1629
+ ].include?(mode) && !@options.fseg_id
1630
+ usage(1, "File segment id must be specified using -F/--fseg-id")
2006
1631
  end
2007
1632
 
2008
- if @options.trace > 0
2009
- BufferCursor.trace!
2010
- end
1633
+ # rubocop:enable Style/IfUnlessModifier
1634
+
1635
+ BufferCursor.trace! if @options.trace.positive?
2011
1636
 
2012
1637
  case mode
2013
1638
  when "system-spaces"
@@ -2027,8 +1652,7 @@ when "space-index-pages-summary"
2027
1652
  when "space-index-fseg-pages-summary"
2028
1653
  space_index_fseg_pages_summary(space, @options.fseg_id)
2029
1654
  when "space-index-pages-free-plot"
2030
- file_name = space.name.sub(".ibd", "").sub(/[^a-zA-Z0-9_]/, "_")
2031
- space_index_pages_free_plot(space, file_name, @options.page || 0)
1655
+ space_index_pages_free_plot(space, @options.page || 0)
2032
1656
  when "space-page-type-regions"
2033
1657
  space_page_type_regions(space, @options.page || 0)
2034
1658
  when "space-page-type-summary"
@@ -2043,12 +1667,8 @@ when "space-extents"
2043
1667
  space_extents(space)
2044
1668
  when "space-extents-illustrate"
2045
1669
  space_extents_illustrate(space)
2046
- when "space-extents-illustrate-svg"
2047
- space_extents_illustrate_svg(space)
2048
1670
  when "space-lsn-age-illustrate"
2049
1671
  space_lsn_age_illustrate(space)
2050
- when "space-lsn-age-illustrate-svg"
2051
- space_lsn_age_illustrate_svg(space)
2052
1672
  when "space-inodes-fseg-id"
2053
1673
  space_inodes_fseg_id(space)
2054
1674
  when "space-inodes-summary"