innodb_ruby 0.9.13 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +5 -6
- data/bin/innodb_log +14 -19
- data/bin/innodb_space +592 -745
- data/lib/innodb.rb +5 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -325
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -275
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +191 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page.rb +446 -291
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +965 -924
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +386 -391
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- metadata +112 -21
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 21b726dd85c361262f577f663a70b008acb56f45fc2a1632147bc94eb622141e
|
4
|
+
data.tar.gz: 720eda759132e67b03c8bcd9c0e50a90625f3de214048e870875cb964bdd8bbe
|
5
|
+
SHA512:
|
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
|
-
#
|
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 |
|
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,9 +59,10 @@ 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
|
-
Usage: innodb_log [-d] [-
|
65
|
+
Usage: innodb_log [-d] [-l <lsn>] -f <log file> <mode>
|
78
66
|
|
79
67
|
--help, -?
|
80
68
|
Print this usage text.
|
@@ -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)
|
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
|
-
#
|
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(
|
21
|
-
deltas =
|
22
|
-
steps =
|
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|
|
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 =
|
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
|
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)
|
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
|
95
|
-
|
96
|
-
end
|
87
|
+
def center(text, width)
|
88
|
+
return text if text.size >= width
|
97
89
|
|
98
|
-
|
99
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
151
|
-
|
120
|
+
puts "%-12s%-64s" % %w[
|
121
|
+
start_page
|
122
|
+
page_used_bitmap
|
152
123
|
]
|
153
124
|
|
154
125
|
list.each do |entry|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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" % [
|
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
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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|
|
@@ -339,12 +309,16 @@ def space_index_pages_summary(space, start_page)
|
|
339
309
|
print_index_page_summary(space.each_page(start_page))
|
340
310
|
end
|
341
311
|
|
312
|
+
def space_index_fseg_pages_summary(space, fseg_id)
|
313
|
+
print_index_page_summary(space.inode(fseg_id).each_page)
|
314
|
+
end
|
315
|
+
|
342
316
|
def space_page_type_regions(space, start_page)
|
343
|
-
puts "%-12s%-12s%-12s%-20s" % [
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
317
|
+
puts "%-12s%-12s%-12s%-20s" % %w[
|
318
|
+
start
|
319
|
+
end
|
320
|
+
count
|
321
|
+
type
|
348
322
|
]
|
349
323
|
|
350
324
|
space.each_page_type_region(start_page) do |region|
|
@@ -363,16 +337,16 @@ def space_page_type_summary(space, start_page)
|
|
363
337
|
page_count = 0
|
364
338
|
# A Hash of page type => count.
|
365
339
|
page_type = Hash.new(0)
|
366
|
-
space.each_page(start_page) do |
|
340
|
+
space.each_page(start_page) do |_page_number, page|
|
367
341
|
page_count += 1
|
368
342
|
page_type[page.type] += 1
|
369
343
|
end
|
370
344
|
|
371
|
-
puts "%-20s%-12s%-12s%-20s" % [
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
345
|
+
puts "%-20s%-12s%-12s%-20s" % %w[
|
346
|
+
type
|
347
|
+
count
|
348
|
+
percent
|
349
|
+
description
|
376
350
|
]
|
377
351
|
|
378
352
|
# Sort the page type Hash by count, descending.
|
@@ -380,7 +354,7 @@ def space_page_type_summary(space, start_page)
|
|
380
354
|
puts "%-20s%-12i%-12.2f%-20s" % [
|
381
355
|
type,
|
382
356
|
type_count,
|
383
|
-
100.0 * (type_count.to_f / page_count
|
357
|
+
100.0 * (type_count.to_f / page_count),
|
384
358
|
Innodb::Page::PAGE_TYPE[type][:description],
|
385
359
|
]
|
386
360
|
end
|
@@ -393,16 +367,14 @@ end
|
|
393
367
|
def space_list_iterate(space, list_name)
|
394
368
|
fsp = space.page(0).fsp_header
|
395
369
|
|
396
|
-
|
397
|
-
raise "List '#{list_name}' doesn't exist"
|
398
|
-
end
|
370
|
+
raise "List '#{list_name}' doesn't exist" unless fsp[list_name].is_a?(Innodb::List)
|
399
371
|
|
400
372
|
case fsp[list_name]
|
401
373
|
when Innodb::List::Xdes
|
402
374
|
print_xdes_list(space, fsp[list_name])
|
403
375
|
when Innodb::List::Inode
|
404
|
-
puts "%-12s" % [
|
405
|
-
|
376
|
+
puts "%-12s" % %w[
|
377
|
+
page
|
406
378
|
]
|
407
379
|
fsp[list_name].each do |page|
|
408
380
|
puts "%-12i" % [
|
@@ -413,23 +385,25 @@ def space_list_iterate(space, list_name)
|
|
413
385
|
end
|
414
386
|
|
415
387
|
def space_indexes(innodb_system, space)
|
416
|
-
puts "%-12s%-32s%-12s%-12s%-12s%-12s%-12s" % [
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
424
397
|
]
|
425
398
|
|
426
399
|
space.each_index do |index|
|
427
400
|
index.each_fseg do |fseg_name, fseg|
|
428
|
-
puts "%-12i%-32s%-12i%-12s%-12i%-12i%-12s" % [
|
401
|
+
puts "%-12i%-32s%-12i%-12s%-12i%-12i%-12i%-12s" % [
|
429
402
|
index.id,
|
430
403
|
innodb_system ? innodb_system.index_name_by_id(index.id) : "",
|
431
404
|
index.root.offset,
|
432
405
|
fseg_name,
|
406
|
+
fseg.fseg_id,
|
433
407
|
fseg.used_pages,
|
434
408
|
fseg.total_pages,
|
435
409
|
"%.2f%%" % fseg.fill_factor,
|
@@ -438,17 +412,15 @@ def space_indexes(innodb_system, space)
|
|
438
412
|
end
|
439
413
|
end
|
440
414
|
|
441
|
-
def space_index_pages_free_plot(space,
|
442
|
-
unless require "gnuplot"
|
443
|
-
raise "Couldn't load gnuplot. Is it installed?"
|
444
|
-
end
|
415
|
+
def space_index_pages_free_plot(space, start_page)
|
416
|
+
raise "Could not load gnuplot. Is it installed?" unless require "gnuplot"
|
445
417
|
|
446
|
-
index_data = {0 => {:
|
418
|
+
index_data = { 0 => { x: [], y: [] } }
|
447
419
|
|
448
420
|
space.each_page(start_page) do |page_number, page|
|
449
421
|
case page.type
|
450
422
|
when :INDEX
|
451
|
-
data = (index_data[page.page_header[:index_id]] ||= {:
|
423
|
+
data = (index_data[page.page_header[:index_id]] ||= { x: [], y: [] })
|
452
424
|
data[:x] << page_number
|
453
425
|
data[:y] << page.free_space
|
454
426
|
when :ALLOCATED
|
@@ -457,15 +429,17 @@ def space_index_pages_free_plot(space, image, start_page)
|
|
457
429
|
end
|
458
430
|
end
|
459
431
|
|
460
|
-
|
432
|
+
image_name = space.name.sub(".ibd", "").gsub(/[^a-zA-Z0-9_]/, "_").sub(/\A_+/, "")
|
433
|
+
image_file = "#{image_name}_free.png"
|
434
|
+
|
461
435
|
# Aim for one horizontal pixel per extent, but min 1k and max 10k width.
|
462
|
-
image_width = [
|
436
|
+
image_width = [10_000, [1_000, space.pages / space.pages_per_extent].max].min
|
463
437
|
|
464
438
|
Gnuplot.open do |gp|
|
465
439
|
Gnuplot::Plot.new(gp) do |plot|
|
466
440
|
plot.terminal "png size #{image_width}, 800"
|
467
441
|
plot.output image_file
|
468
|
-
plot.title
|
442
|
+
plot.title image_name.gsub("_", " ")
|
469
443
|
plot.key "reverse left top box horizontal Left textcolor variable"
|
470
444
|
plot.ylabel "free space per page"
|
471
445
|
plot.xlabel "page number"
|
@@ -475,7 +449,7 @@ def space_index_pages_free_plot(space, image, start_page)
|
|
475
449
|
index_data.sort.each do |id, data|
|
476
450
|
plot.data << Gnuplot::DataSet.new([data[:x], data[:y]]) do |ds|
|
477
451
|
ds.with = "dots"
|
478
|
-
ds.title = id
|
452
|
+
ds.title = id.zero? ? "Unallocated" : "Index #{id}"
|
479
453
|
end
|
480
454
|
end
|
481
455
|
|
@@ -488,13 +462,52 @@ def space_extents(space)
|
|
488
462
|
print_xdes_list(space, space.each_xdes)
|
489
463
|
end
|
490
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
|
+
|
491
503
|
# Illustrate the space by printing each extent and for each page, printing a
|
492
504
|
# filled block colored based on the index the page is part of. Print a legend
|
493
505
|
# for the colors used afterwards.
|
494
506
|
def space_extents_illustrate(space)
|
495
507
|
width = space.pages_per_extent
|
496
508
|
puts
|
497
|
-
puts "%12s
|
509
|
+
puts "%12s %-#{width}s " % ["", center(space.name, width)]
|
510
|
+
puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
|
498
511
|
|
499
512
|
identifiers = {}
|
500
513
|
count_by_identifier = Hash.new(0)
|
@@ -502,264 +515,46 @@ def space_extents_illustrate(space)
|
|
502
515
|
space.each_xdes do |entry|
|
503
516
|
puts "%12i │%-#{width}s│" % [
|
504
517
|
entry.xdes[:start_page],
|
505
|
-
|
506
|
-
if page_number < space.pages
|
507
|
-
used_fraction = 1.0
|
508
|
-
identifier = nil
|
509
|
-
if page_status[:free]
|
510
|
-
used_fraction = 0.0
|
511
|
-
else
|
512
|
-
page = space.page(page_number)
|
513
|
-
if page.respond_to?(:used_space)
|
514
|
-
used_fraction = page.used_space.to_f / page.size.to_f
|
515
|
-
end
|
516
|
-
if page.respond_to?(:index_id)
|
517
|
-
identifier = page.index_id
|
518
|
-
unless identifiers[identifier]
|
519
|
-
identifiers[identifier] = (page.index_id == Innodb::IbufIndex::INDEX_ID) ?
|
520
|
-
"Insert Buffer Index" :
|
521
|
-
"Index #{page.index_id}"
|
522
|
-
if space.innodb_system
|
523
|
-
table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
|
524
|
-
if table && index
|
525
|
-
identifiers[identifier] += " (%s.%s)" % [table, index]
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
end
|
530
|
-
end
|
531
|
-
bitmap += filled_block(used_fraction, identifier)
|
532
|
-
if used_fraction != 0.0
|
533
|
-
count_by_identifier[identifier] += 1
|
534
|
-
else
|
535
|
-
count_by_identifier[:free] += 1
|
536
|
-
end
|
537
|
-
else
|
538
|
-
bitmap += " "
|
539
|
-
end
|
540
|
-
bitmap
|
541
|
-
},
|
518
|
+
space_extents_illustrate_page_status(space, entry, count_by_identifier, identifiers),
|
542
519
|
]
|
543
520
|
end
|
544
521
|
total_pages = count_by_identifier.values.reduce(:+)
|
545
522
|
|
546
|
-
puts "%12s ╰%-#{width}s╯" % [
|
523
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
547
524
|
|
548
525
|
puts
|
549
526
|
puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
|
550
527
|
puts " %-62s %8s %8s" % [
|
551
|
-
"Page Type",
|
528
|
+
"Page Type",
|
529
|
+
"Pages",
|
530
|
+
"Ratio",
|
552
531
|
]
|
553
532
|
puts " %s %-60s %8i %7.2f%%" % [
|
554
533
|
filled_block(1.0, nil),
|
555
534
|
"System",
|
556
535
|
count_by_identifier[nil],
|
557
|
-
100.0 * (count_by_identifier[nil].to_f / total_pages
|
536
|
+
100.0 * (count_by_identifier[nil].to_f / total_pages),
|
558
537
|
]
|
559
538
|
identifiers.sort.each do |identifier, description|
|
560
539
|
puts " %s %-60s %8i %7.2f%%" % [
|
561
540
|
filled_block(1.0, identifier),
|
562
541
|
description,
|
563
542
|
count_by_identifier[identifier],
|
564
|
-
100.0 * (count_by_identifier[identifier].to_f / total_pages
|
543
|
+
100.0 * (count_by_identifier[identifier].to_f / total_pages),
|
565
544
|
]
|
566
545
|
end
|
567
546
|
puts " %s %-60s %8i %7.2f%%" % [
|
568
547
|
filled_block(0.0, nil),
|
569
548
|
"Free space",
|
570
549
|
count_by_identifier[:free],
|
571
|
-
100.0 * (count_by_identifier[:free].to_f / total_pages
|
550
|
+
100.0 * (count_by_identifier[:free].to_f / total_pages),
|
572
551
|
]
|
573
552
|
puts
|
574
553
|
end
|
575
554
|
|
576
|
-
def svg_extent_legend(x, y, block_size, color=nil, description=nil, pages=nil, ratio=nil)
|
577
|
-
[
|
578
|
-
svg("rect", {
|
579
|
-
"y" => y,
|
580
|
-
"x" => x,
|
581
|
-
"width" => block_size,
|
582
|
-
"height" => block_size,
|
583
|
-
"fill" => color ? color : "white",
|
584
|
-
"stroke" => description ? "black" : "none",
|
585
|
-
}),
|
586
|
-
svg("text", {
|
587
|
-
"y" => y + block_size - 4,
|
588
|
-
"x" => x + (description ? block_size + 5 : 0),
|
589
|
-
"font-family" => "monospace",
|
590
|
-
"font-size" => block_size,
|
591
|
-
"font-weight" => description ? "normal" : "bold",
|
592
|
-
"text-anchor" => "start",
|
593
|
-
}, description ? description : "Page Type"),
|
594
|
-
svg("text", {
|
595
|
-
"y" => y + block_size - 4,
|
596
|
-
"x" => x + block_size + 5 + (40 * block_size),
|
597
|
-
"font-family" => "monospace",
|
598
|
-
"font-size" => block_size,
|
599
|
-
"font-weight" => description ? "normal" : "bold",
|
600
|
-
"text-anchor" => "end",
|
601
|
-
}, pages ? pages : "Pages"),
|
602
|
-
svg("text", {
|
603
|
-
"y" => y + block_size - 4,
|
604
|
-
"x" => x + block_size + 5 + (40 * block_size) + (10 * block_size),
|
605
|
-
"font-family" => "monospace",
|
606
|
-
"font-size" => block_size,
|
607
|
-
"font-weight" => description ? "normal" : "bold",
|
608
|
-
"text-anchor" => "end",
|
609
|
-
}, ratio ? ("%7.2f%%" % [ratio]) : "Ratio"),
|
610
|
-
].join("\n")
|
611
|
-
end
|
612
|
-
|
613
|
-
# Illustrate the space by printing each extent and for each page, printing a
|
614
|
-
# filled block colored based on the index the page is part of. Print a legend
|
615
|
-
# for the colors used afterwards.
|
616
|
-
def space_extents_illustrate_svg(space)
|
617
|
-
width = space.pages_per_extent
|
618
|
-
|
619
|
-
puts "<?xml version=\"1.0\"?>"
|
620
|
-
puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
|
621
|
-
|
622
|
-
identifiers = {}
|
623
|
-
count_by_identifier = Hash.new(0)
|
624
|
-
|
625
|
-
block_size = 16
|
626
|
-
graphic_x = 48
|
627
|
-
graphic_y = 16
|
628
|
-
|
629
|
-
puts svg("text", {
|
630
|
-
"y" => graphic_y - 3,
|
631
|
-
"x" => graphic_x - 7,
|
632
|
-
"font-family" => "monospace",
|
633
|
-
"font-size" => block_size,
|
634
|
-
"font-weight" => "bold",
|
635
|
-
"text-anchor" => "end",
|
636
|
-
}, "Page")
|
637
|
-
|
638
|
-
block_x = 0
|
639
|
-
block_y = 0
|
640
|
-
space.each_xdes do |entry|
|
641
|
-
block_x = 0
|
642
|
-
|
643
|
-
puts svg("text", {
|
644
|
-
"y" => graphic_y + block_y + block_size,
|
645
|
-
"x" => graphic_x - 7,
|
646
|
-
"font-family" => "monospace",
|
647
|
-
"font-size" => block_size,
|
648
|
-
"text-anchor" => "end",
|
649
|
-
}, entry.xdes[:start_page])
|
650
|
-
|
651
|
-
entry.each_page_status do |page_number, page_status|
|
652
|
-
if page_number < space.pages
|
653
|
-
used_fraction = 1.0
|
654
|
-
identifier = nil
|
655
|
-
if page_status[:free]
|
656
|
-
used_fraction = 0.0
|
657
|
-
else
|
658
|
-
page = space.page(page_number)
|
659
|
-
if page.respond_to?(:used_space)
|
660
|
-
used_fraction = page.used_space.to_f / page.size.to_f
|
661
|
-
end
|
662
|
-
if page.respond_to?(:index_id)
|
663
|
-
identifier = page.index_id
|
664
|
-
unless identifiers[identifier]
|
665
|
-
identifiers[identifier] = (page.index_id == Innodb::IbufIndex::INDEX_ID) ?
|
666
|
-
"Insert Buffer Index" :
|
667
|
-
"Index #{page.index_id}"
|
668
|
-
if space.innodb_system
|
669
|
-
table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
|
670
|
-
if table && index
|
671
|
-
identifiers[identifier] += " (%s.%s)" % [table, index]
|
672
|
-
end
|
673
|
-
end
|
674
|
-
end
|
675
|
-
end
|
676
|
-
end
|
677
|
-
if used_fraction != 0.0
|
678
|
-
count_by_identifier[identifier] += 1
|
679
|
-
else
|
680
|
-
count_by_identifier[:free] += 1
|
681
|
-
end
|
682
|
-
|
683
|
-
block_height = block_size * used_fraction
|
684
|
-
color = "black"
|
685
|
-
if identifier
|
686
|
-
color = "#" + RGBHEX_COLORS_RANDOM[(identifier * COLOR_SPACING_PRIME) % RGBHEX_COLORS_RANDOM.size]
|
687
|
-
end
|
688
|
-
puts svg("rect", {
|
689
|
-
"x" => graphic_x + block_x,
|
690
|
-
"y" => graphic_y + block_y + (block_size - block_height),
|
691
|
-
"width" => block_size,
|
692
|
-
"height" => block_height,
|
693
|
-
"fill" => color,
|
694
|
-
})
|
695
|
-
end
|
696
|
-
block_x += block_size
|
697
|
-
end
|
698
|
-
block_y += block_size
|
699
|
-
end
|
700
|
-
|
701
|
-
puts svg("path", {
|
702
|
-
"stroke" => "black",
|
703
|
-
"stroke-width" => 1,
|
704
|
-
"fill" => "none",
|
705
|
-
"d" => svg_path_rounded_rect(
|
706
|
-
graphic_x,
|
707
|
-
graphic_y,
|
708
|
-
block_x,
|
709
|
-
block_y,
|
710
|
-
4
|
711
|
-
),
|
712
|
-
})
|
713
|
-
|
714
|
-
block_x = 0
|
715
|
-
block_y += 10
|
716
|
-
puts svg_extent_legend(
|
717
|
-
graphic_x + block_x,
|
718
|
-
graphic_y + block_y,
|
719
|
-
block_size,
|
720
|
-
)
|
721
|
-
block_y += block_size + 2
|
722
|
-
|
723
|
-
puts svg_extent_legend(
|
724
|
-
graphic_x + block_x,
|
725
|
-
graphic_y + block_y,
|
726
|
-
block_size,
|
727
|
-
"black",
|
728
|
-
"System",
|
729
|
-
count_by_identifier[nil],
|
730
|
-
100.0 * (count_by_identifier[nil].to_f / space.pages.to_f)
|
731
|
-
)
|
732
|
-
block_y += block_size + 2
|
733
|
-
|
734
|
-
identifiers.sort.each do |identifier, description|
|
735
|
-
puts svg_extent_legend(
|
736
|
-
graphic_x + block_x,
|
737
|
-
graphic_y + block_y,
|
738
|
-
block_size,
|
739
|
-
"#" + RGBHEX_COLORS_RANDOM[(identifier * COLOR_SPACING_PRIME) % RGBHEX_COLORS_RANDOM.size],
|
740
|
-
description,
|
741
|
-
count_by_identifier[identifier],
|
742
|
-
100.0 * (count_by_identifier[identifier].to_f / space.pages.to_f)
|
743
|
-
)
|
744
|
-
block_y += block_size + 2
|
745
|
-
end
|
746
|
-
|
747
|
-
puts svg_extent_legend(
|
748
|
-
graphic_x + block_x,
|
749
|
-
graphic_y + block_y,
|
750
|
-
block_size,
|
751
|
-
"white",
|
752
|
-
"Free space",
|
753
|
-
count_by_identifier[:free],
|
754
|
-
100.0 * (count_by_identifier[:free].to_f / space.pages.to_f)
|
755
|
-
)
|
756
|
-
|
757
|
-
puts "</svg>"
|
758
|
-
end
|
759
|
-
|
760
|
-
|
761
555
|
def space_lsn_age_illustrate(space)
|
762
556
|
colors = ANSI_COLORS_HEATMAP
|
557
|
+
width = @options.illustration_line_width
|
763
558
|
|
764
559
|
# Calculate the minimum and maximum LSN in the space. This is pretty
|
765
560
|
# inefficient as we end up scanning all pages twice.
|
@@ -767,70 +562,57 @@ def space_lsn_age_illustrate(space)
|
|
767
562
|
|
768
563
|
lsn_min = lsn_max = space.page(0).lsn
|
769
564
|
space.each_page do |page_number, page|
|
770
|
-
if page.lsn
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
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
|
775
570
|
end
|
776
571
|
lsn_delta = lsn_max - lsn_min
|
777
572
|
|
778
573
|
puts
|
779
|
-
puts "%12s
|
574
|
+
puts "%12s %-#{width}s " % ["", center(space.name, width)]
|
575
|
+
puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
|
780
576
|
|
781
577
|
start_page = 0
|
782
|
-
page_lsn.each_slice(
|
783
|
-
puts "%12i
|
578
|
+
page_lsn.each_slice(width) do |slice|
|
579
|
+
puts "%12i │%-#{width}s│" % [
|
784
580
|
start_page,
|
785
|
-
slice.inject("")
|
581
|
+
slice.inject("") do |line, lsn|
|
786
582
|
if lsn
|
787
|
-
age_ratio = (lsn - lsn_min).to_f / lsn_delta
|
583
|
+
age_ratio = (lsn - lsn_min).to_f / lsn_delta
|
788
584
|
color = colors[(age_ratio * colors.size.to_f).floor]
|
789
585
|
line += ansi_color(color, filled_block(1.0, nil))
|
790
586
|
else
|
791
587
|
line += " "
|
792
588
|
end
|
793
589
|
line
|
794
|
-
|
590
|
+
end,
|
795
591
|
]
|
796
|
-
start_page +=
|
592
|
+
start_page += width
|
797
593
|
end
|
798
594
|
|
799
|
-
puts "%12s
|
800
|
-
|
801
|
-
lsn_legend = "<" + ("─" * (colors.size - 2)) + ">"
|
802
|
-
|
803
|
-
begin
|
804
|
-
# Try to optionally replace the boring lsn_legend with a histogram of
|
805
|
-
# page age distribution. If histogram/array is not available, move on.
|
595
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
806
596
|
|
807
|
-
|
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
|
808
599
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
lsn_legend = ""
|
815
|
-
lsn_freq.each do |freq|
|
816
|
-
freq_norm = freq / lsn_freq_delta
|
817
|
-
if freq_norm > 0.0
|
818
|
-
lsn_legend << filled_block(freq_norm)
|
819
|
-
else
|
820
|
-
# Avoid the "empty" block used for 0.0.
|
821
|
-
lsn_legend << " "
|
822
|
-
end
|
823
|
-
end
|
824
|
-
rescue LoadError
|
825
|
-
# 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) : " ")
|
826
604
|
end
|
827
605
|
|
828
606
|
puts
|
829
|
-
puts "
|
607
|
+
puts "LSN Age Histogram (%s = ~%d pages):" % [
|
608
|
+
filled_block(1.0, nil),
|
609
|
+
(space.pages.to_f / colors.size).round,
|
610
|
+
]
|
830
611
|
puts " %12s %s %-12s" % [
|
831
612
|
"Min LSN",
|
832
|
-
|
833
|
-
"Max LSN"
|
613
|
+
lsn_age_histogram,
|
614
|
+
"Max LSN",
|
615
|
+
]
|
834
616
|
puts " %12i %s %-12i" % [
|
835
617
|
lsn_min,
|
836
618
|
colors.map { |c| ansi_color(c, filled_block(1.0, nil)) }.join,
|
@@ -838,125 +620,6 @@ def space_lsn_age_illustrate(space)
|
|
838
620
|
]
|
839
621
|
end
|
840
622
|
|
841
|
-
def space_lsn_age_illustrate_svg(space)
|
842
|
-
colors = RGBHEX_COLORS_HEATMAP
|
843
|
-
|
844
|
-
puts "<?xml version=\"1.0\"?>"
|
845
|
-
puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
|
846
|
-
|
847
|
-
# Calculate the minimum and maximum LSN in the space. This is pretty
|
848
|
-
# inefficient as we end up scanning all pages twice.
|
849
|
-
page_lsn = Array.new(space.pages)
|
850
|
-
|
851
|
-
lsn_min = lsn_max = space.page(0).lsn
|
852
|
-
space.each_page do |page_number, page|
|
853
|
-
if page.lsn != 0
|
854
|
-
page_lsn[page_number] = page.lsn
|
855
|
-
lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
|
856
|
-
lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
|
857
|
-
end
|
858
|
-
end
|
859
|
-
lsn_delta = lsn_max - lsn_min
|
860
|
-
|
861
|
-
block_size = 16
|
862
|
-
graphic_x = 48
|
863
|
-
graphic_y = 16
|
864
|
-
|
865
|
-
block_x = 0
|
866
|
-
block_y = 0
|
867
|
-
|
868
|
-
puts svg("text", {
|
869
|
-
"y" => graphic_y - 3,
|
870
|
-
"x" => graphic_x - 7,
|
871
|
-
"font-family" => "monospace",
|
872
|
-
"font-size" => block_size,
|
873
|
-
"font-weight" => "bold",
|
874
|
-
"text-anchor" => "end",
|
875
|
-
}, "Page")
|
876
|
-
|
877
|
-
start_page = 0
|
878
|
-
page_lsn.each_slice(64) do |slice|
|
879
|
-
block_x = 0
|
880
|
-
slice.each do |lsn|
|
881
|
-
rgbhex = ""
|
882
|
-
if lsn
|
883
|
-
age_ratio = (lsn - lsn_min).to_f / lsn_delta.to_f
|
884
|
-
color = colors[(age_ratio * colors.size.to_f).floor]
|
885
|
-
end
|
886
|
-
puts svg("rect", {
|
887
|
-
"y" => graphic_y + block_y,
|
888
|
-
"x" => graphic_x + block_x,
|
889
|
-
"width" => block_size,
|
890
|
-
"height" => block_size,
|
891
|
-
"fill" => color ? "#" + color : "black",
|
892
|
-
})
|
893
|
-
block_x += block_size
|
894
|
-
end
|
895
|
-
puts svg("text", {
|
896
|
-
"y" => graphic_y + block_y + block_size,
|
897
|
-
"x" => graphic_x - 7,
|
898
|
-
"font-family" => "monospace",
|
899
|
-
"font-size" => block_size,
|
900
|
-
"text-anchor" => "end",
|
901
|
-
}, start_page)
|
902
|
-
block_y += block_size
|
903
|
-
start_page += 64
|
904
|
-
end
|
905
|
-
|
906
|
-
puts svg("path", {
|
907
|
-
"stroke" => "black",
|
908
|
-
"stroke-width" => 1,
|
909
|
-
"fill" => "none",
|
910
|
-
"d" => svg_path_rounded_rect(
|
911
|
-
graphic_x,
|
912
|
-
graphic_y,
|
913
|
-
block_x,
|
914
|
-
block_y,
|
915
|
-
4
|
916
|
-
),
|
917
|
-
})
|
918
|
-
|
919
|
-
block_x = 0
|
920
|
-
block_y += 16
|
921
|
-
puts svg("text", {
|
922
|
-
"y" => graphic_y + block_y + block_size - 4,
|
923
|
-
"x" => graphic_x + block_x,
|
924
|
-
"font-family" => "monospace",
|
925
|
-
"font-size" => block_size,
|
926
|
-
"text-anchor" => "start",
|
927
|
-
}, lsn_min)
|
928
|
-
color_width = ((64.0 * block_size.to_f) / colors.size.to_f).round
|
929
|
-
colors.each do |color|
|
930
|
-
puts svg("rect", {
|
931
|
-
"y" => graphic_y + block_y + block_size,
|
932
|
-
"x" => graphic_x + block_x,
|
933
|
-
"width" => color_width,
|
934
|
-
"height" => block_size,
|
935
|
-
"fill" => "#" + color,
|
936
|
-
})
|
937
|
-
block_x += color_width
|
938
|
-
end
|
939
|
-
puts svg("text", {
|
940
|
-
"y" => graphic_y + block_y + block_size - 4,
|
941
|
-
"x" => graphic_x + block_x,
|
942
|
-
"font-family" => "monospace",
|
943
|
-
"font-size" => block_size,
|
944
|
-
"text-anchor" => "end",
|
945
|
-
}, lsn_max)
|
946
|
-
|
947
|
-
puts svg("text", {
|
948
|
-
"y" => graphic_y + block_y + block_size - 4,
|
949
|
-
"x" => graphic_x + (block_x / 2),
|
950
|
-
"font-family" => "monospace",
|
951
|
-
"font-weight" => "bold",
|
952
|
-
"font-size" => block_size,
|
953
|
-
"text-anchor" => "middle",
|
954
|
-
}, "LSN Age")
|
955
|
-
|
956
|
-
|
957
|
-
puts "</svg>\n"
|
958
|
-
end
|
959
|
-
|
960
623
|
def print_inode_summary(inode)
|
961
624
|
puts "INODE fseg_id=%d, pages=%d, frag=%d, full=%d, not_full=%d, free=%d" % [
|
962
625
|
inode.fseg_id,
|
@@ -969,6 +632,7 @@ def print_inode_summary(inode)
|
|
969
632
|
end
|
970
633
|
|
971
634
|
def print_inode_detail(inode)
|
635
|
+
# rubocop:disable Layout/LineLength
|
972
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)" % [
|
973
637
|
inode.fseg_id,
|
974
638
|
inode.total_pages,
|
@@ -983,6 +647,13 @@ def print_inode_detail(inode)
|
|
983
647
|
inode.free.length,
|
984
648
|
inode.free.each.to_a.map { |x| "#{x.start_page}-#{x.end_page}" }.join(", "),
|
985
649
|
]
|
650
|
+
# rubocop:enable Layout/LineLength
|
651
|
+
end
|
652
|
+
|
653
|
+
def space_inodes_fseg_id(space)
|
654
|
+
space.each_inode do |inode|
|
655
|
+
puts inode.fseg_id
|
656
|
+
end
|
986
657
|
end
|
987
658
|
|
988
659
|
def space_inodes_summary(space)
|
@@ -1029,25 +700,23 @@ def page_account(innodb_system, space, page_number)
|
|
1029
700
|
|
1030
701
|
xdes_status = xdes.page_status(page_number)
|
1031
702
|
puts " Page is marked as %s in extent descriptor." % [
|
1032
|
-
xdes_status[:free] ?
|
703
|
+
xdes_status[:free] ? "free" : "used",
|
1033
704
|
]
|
1034
705
|
|
1035
706
|
space.each_xdes_list do |name, list|
|
1036
|
-
if list.include?
|
1037
|
-
puts " Extent is in #{name} list of space."
|
1038
|
-
end
|
707
|
+
puts " Extent is in #{name} list of space." if list.include?(xdes)
|
1039
708
|
end
|
1040
709
|
|
1041
710
|
page_inode = nil
|
1042
711
|
space.each_inode do |inode|
|
1043
712
|
inode.each_list do |name, list|
|
1044
|
-
if list.include?
|
713
|
+
if list.include?(xdes)
|
1045
714
|
page_inode = inode
|
1046
715
|
puts " Extent is in #{name} list of fseg #{inode.fseg_id}."
|
1047
716
|
end
|
1048
717
|
end
|
1049
718
|
|
1050
|
-
if inode.frag_array.include?
|
719
|
+
if inode.frag_array.include?(page_number) # rubocop:disable Style/Next
|
1051
720
|
page_inode = inode
|
1052
721
|
puts " Page is in fragment array of fseg %d." % [
|
1053
722
|
inode.fseg_id,
|
@@ -1057,65 +726,213 @@ def page_account(innodb_system, space, page_number)
|
|
1057
726
|
|
1058
727
|
space.each_index do |index|
|
1059
728
|
index.each_fseg do |fseg_name, fseg|
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
end
|
1068
|
-
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
|
1069
736
|
end
|
1070
737
|
end
|
1071
738
|
end
|
1072
739
|
|
1073
|
-
if space.system_space?
|
1074
|
-
if page_inode == space.trx_sys.fseg
|
1075
|
-
|
1076
|
-
end
|
1077
|
-
|
1078
|
-
if page_inode == space.trx_sys.doublewrite[:fseg]
|
1079
|
-
puts " Fseg is doublewrite buffer."
|
1080
|
-
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]
|
1081
743
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
if page_inode == fseg
|
1086
|
-
puts " Index is #{table_name}.#{index_name} of data dictionary."
|
1087
|
-
end
|
1088
|
-
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
|
1089
747
|
end
|
1090
748
|
end
|
1091
749
|
|
1092
750
|
space.trx_sys.rsegs.each_with_index do |rseg_slot, index|
|
1093
|
-
if page.fil_header
|
1094
|
-
|
1095
|
-
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}."
|
1096
753
|
end
|
1097
754
|
end
|
1098
755
|
end
|
1099
756
|
end
|
1100
757
|
|
1101
|
-
def
|
1102
|
-
|
1103
|
-
|
758
|
+
def page_validate_index(page)
|
759
|
+
page_is_valid = true
|
760
|
+
|
761
|
+
print "Parsing all records in page... "
|
762
|
+
records = page.each_record.to_a
|
763
|
+
puts "done."
|
764
|
+
|
765
|
+
directory_offsets = page.each_directory_offset.to_a
|
766
|
+
record_offsets = records.map(&:offset)
|
767
|
+
|
768
|
+
invalid_directory_entries = directory_offsets.reject { |n| record_offsets.include?(n) }
|
769
|
+
|
770
|
+
unless invalid_directory_entries.empty?
|
771
|
+
page_is_valid = false
|
772
|
+
puts "Invalid page directory entries (offsets not to valid records):"
|
773
|
+
invalid_directory_entries.each do |offset|
|
774
|
+
puts " slot %d, offset %d" % [
|
775
|
+
page.offset_is_directory_slot?(offset),
|
776
|
+
offset,
|
777
|
+
]
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
# Read all records corresponding to valid directory entries.
|
782
|
+
directory_records = directory_offsets.reject { |o| invalid_directory_entries.include?(o) }.map { |o| page.record(o) }
|
783
|
+
|
784
|
+
misordered_directory_entries = []
|
785
|
+
prev = nil
|
786
|
+
directory_records.each do |rec|
|
787
|
+
unless prev
|
788
|
+
prev = rec
|
789
|
+
next
|
790
|
+
end
|
791
|
+
if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
|
792
|
+
page_is_valid = false
|
793
|
+
misordered_directory_entries << {
|
794
|
+
slot: page.offset_is_directory_slot?(rec.offset),
|
795
|
+
offset: rec.offset,
|
796
|
+
key: rec.key_string,
|
797
|
+
prev_key: prev.key_string,
|
798
|
+
}
|
799
|
+
end
|
800
|
+
prev = rec
|
801
|
+
end
|
802
|
+
unless misordered_directory_entries.empty?
|
803
|
+
puts "Misordered page directory entries (key < prev key):"
|
804
|
+
misordered_directory_entries.each do |entry|
|
805
|
+
puts " slot %d, offset %d, key %s, prev key %s" % [
|
806
|
+
entry[:slot],
|
807
|
+
entry[:offset],
|
808
|
+
entry[:key],
|
809
|
+
entry[:prev_key],
|
810
|
+
]
|
811
|
+
end
|
812
|
+
end
|
813
|
+
|
814
|
+
misordered_records = []
|
815
|
+
prev = nil
|
816
|
+
page.each_record do |rec|
|
817
|
+
unless prev
|
818
|
+
prev = rec
|
819
|
+
next
|
820
|
+
end
|
821
|
+
if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
|
822
|
+
page_is_valid = false
|
823
|
+
misordered_records << {
|
824
|
+
offset: rec.offset,
|
825
|
+
key: rec.key_string,
|
826
|
+
prev_key: prev.key_string,
|
827
|
+
}
|
828
|
+
end
|
829
|
+
prev = rec
|
830
|
+
end
|
831
|
+
unless misordered_records.empty?
|
832
|
+
puts "Misordered records in record list (key < prev key):"
|
833
|
+
misordered_records.each do |entry|
|
834
|
+
puts " offset %d, key %s, prev key %s" % [
|
835
|
+
entry[:offset],
|
836
|
+
entry[:key],
|
837
|
+
entry[:prev_key],
|
838
|
+
]
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
page_is_valid
|
843
|
+
end
|
844
|
+
|
845
|
+
def page_validate(_innodb_system, space, page_number)
|
846
|
+
page_is_valid = true
|
847
|
+
puts "Validating page %d..." % [page_number]
|
848
|
+
|
849
|
+
print "Parsing page... "
|
850
|
+
page = space.page(page_number)
|
851
|
+
puts "done."
|
852
|
+
|
853
|
+
if page.corrupt?
|
854
|
+
page_is_valid = false
|
855
|
+
puts "Page appears to be corrupt:"
|
856
|
+
puts " Stored checksums:"
|
857
|
+
puts " header %10d (0x%08x), type %s" % [
|
858
|
+
page.checksum,
|
859
|
+
page.checksum,
|
860
|
+
page.checksum_type || "unknown",
|
861
|
+
]
|
862
|
+
puts " trailer %10d (0x%08x)" % [
|
863
|
+
page.fil_trailer.checksum,
|
864
|
+
page.fil_trailer.checksum,
|
865
|
+
]
|
866
|
+
puts " Calculated checksums:"
|
867
|
+
puts " crc32 %10d (0x%08x)" % [
|
868
|
+
page.checksum_crc32,
|
869
|
+
page.checksum_crc32,
|
870
|
+
]
|
871
|
+
puts " innodb %10d (0x%08x)" % [
|
872
|
+
page.checksum_innodb,
|
873
|
+
page.checksum_innodb,
|
874
|
+
]
|
875
|
+
end
|
876
|
+
|
877
|
+
if page.torn?
|
878
|
+
page_is_valid = false
|
879
|
+
puts "Page appears to be torn:"
|
880
|
+
puts " Full LSN:"
|
881
|
+
puts " header %d (0x%016x)" % [
|
882
|
+
page.lsn,
|
883
|
+
page.lsn,
|
884
|
+
]
|
885
|
+
puts " Low 32 bits of LSN:"
|
886
|
+
puts " header %10d (0x%08x)" % [
|
887
|
+
page.fil_header.lsn_low32,
|
888
|
+
page.fil_header.lsn_low32,
|
889
|
+
]
|
890
|
+
puts " trailer %10d (0x%08x)" % [
|
891
|
+
page.fil_trailer.lsn_low32,
|
892
|
+
page.fil_trailer.lsn_low32,
|
893
|
+
]
|
1104
894
|
end
|
1105
895
|
|
1106
|
-
|
1107
|
-
|
1108
|
-
"
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
896
|
+
if page.misplaced?
|
897
|
+
page_is_valid = false
|
898
|
+
puts "Page appears to be misplaced:"
|
899
|
+
if page.misplaced_offset?
|
900
|
+
puts " Requested page %d but offset stored in page is %d." % [
|
901
|
+
page_number,
|
902
|
+
page.offset,
|
903
|
+
]
|
904
|
+
end
|
905
|
+
if page.misplaced_space?
|
906
|
+
puts " Space ID %d does not match page stored space ID %d." % [
|
907
|
+
page.space.space_id,
|
908
|
+
page.space_id,
|
909
|
+
]
|
910
|
+
end
|
911
|
+
end
|
912
|
+
|
913
|
+
page_is_valid = false if page.type == :INDEX && !page_validate_index(page)
|
914
|
+
|
915
|
+
puts "Page %d appears to be %s!" % [
|
916
|
+
page_number,
|
917
|
+
page_is_valid ? "valid" : "corrupted",
|
918
|
+
]
|
919
|
+
end
|
920
|
+
|
921
|
+
def page_directory_summary(_space, page)
|
922
|
+
usage(1, "Page must be an index page") if page.type != :INDEX
|
923
|
+
|
924
|
+
puts "%-8s%-8s%-14s%-8s%s" % %w[
|
925
|
+
slot
|
926
|
+
offset
|
927
|
+
type
|
928
|
+
owned
|
929
|
+
key
|
1112
930
|
]
|
1113
931
|
|
1114
932
|
page.directory.each_with_index do |offset, slot|
|
1115
933
|
record = page.record(offset)
|
1116
|
-
key =
|
1117
|
-
|
1118
|
-
end
|
934
|
+
key = %i[conventional node_pointer].include?(record.header[:type]) ? "(%s)" % record.key_string : ""
|
935
|
+
|
1119
936
|
puts "%-8i%-8i%-14s%-8i%s" % [
|
1120
937
|
slot,
|
1121
938
|
offset,
|
@@ -1126,57 +943,63 @@ def page_directory_summary(space, page)
|
|
1126
943
|
end
|
1127
944
|
end
|
1128
945
|
|
1129
|
-
def page_records(
|
946
|
+
def page_records(_space, page)
|
1130
947
|
page.each_record do |record|
|
1131
948
|
puts "Record %i: %s" % [
|
1132
949
|
record.offset,
|
1133
950
|
record.string,
|
1134
951
|
]
|
1135
|
-
puts if record.header[:type] == :conventional
|
1136
952
|
end
|
1137
953
|
end
|
1138
954
|
|
1139
955
|
def page_illustrate(page)
|
1140
956
|
width = 64
|
1141
|
-
|
957
|
+
unknown_page_content = page.type == :INDEX && page.record_describer.nil?
|
958
|
+
blocks = Array.new(page.size, unknown_page_content ? "▞" : " ")
|
1142
959
|
identifiers = {}
|
1143
960
|
identifier_sort = 0
|
1144
961
|
count_by_identifier = Hash.new(0)
|
1145
962
|
|
1146
|
-
page.each_region.
|
1147
|
-
region
|
963
|
+
page.each_region.sort_by(&:offset).each do |region|
|
964
|
+
region.length.times do |n|
|
1148
965
|
identifier = nil
|
1149
966
|
fraction = 0.0
|
1150
|
-
if region
|
1151
|
-
|
1152
|
-
|
1153
|
-
else
|
1154
|
-
fraction = 1.0
|
1155
|
-
end
|
1156
|
-
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
|
1157
970
|
unless identifiers[identifier]
|
1158
971
|
# Prefix an integer <0123> on each name so that the legend can be
|
1159
972
|
# sorted by the appearance of each region in the page.
|
1160
973
|
identifiers[identifier] = "<%04i>%s" % [
|
1161
974
|
identifier_sort,
|
1162
|
-
region
|
975
|
+
region.info,
|
1163
976
|
]
|
1164
977
|
identifier_sort += 1
|
1165
978
|
end
|
1166
979
|
end
|
1167
|
-
blocks[region
|
980
|
+
blocks[region.offset + n] = filled_block(fraction, identifier, BLOCK_CHARS_H)
|
1168
981
|
count_by_identifier[identifier] += 1
|
1169
982
|
end
|
1170
983
|
end
|
1171
984
|
|
1172
985
|
puts
|
1173
|
-
puts "%12s
|
986
|
+
puts "%12s %-#{width}s " % ["", center("Page #{page.offset} (#{page.type})", width)]
|
987
|
+
puts "%12s ╭%-#{width}s╮" % ["Offset", "─" * width]
|
1174
988
|
offset = 0
|
989
|
+
skipped_lines = 0
|
1175
990
|
blocks.each_slice(width) do |slice|
|
1176
|
-
|
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
|
1177
1000
|
offset += width
|
1178
1001
|
end
|
1179
|
-
puts "%12s ╰%-#{width}s╯" % [
|
1002
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
1180
1003
|
|
1181
1004
|
puts
|
1182
1005
|
puts "Legend (%s = 1 byte):" % [filled_block(1.0, nil)]
|
@@ -1185,47 +1008,50 @@ def page_illustrate(page)
|
|
1185
1008
|
"Bytes",
|
1186
1009
|
"Ratio",
|
1187
1010
|
]
|
1188
|
-
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|
|
1189
1012
|
puts " %s %-30s %8i %7.2f%%" % [
|
1190
1013
|
filled_block(1.0, identifier),
|
1191
1014
|
description.gsub(/^<\d+>/, ""),
|
1192
1015
|
count_by_identifier[identifier],
|
1193
|
-
100.0 * (count_by_identifier[identifier].to_f / page.size
|
1016
|
+
100.0 * (count_by_identifier[identifier].to_f / page.size),
|
1194
1017
|
]
|
1195
1018
|
end
|
1196
1019
|
puts " %s %-30s %8i %7.2f%%" % [
|
1197
1020
|
filled_block(0.0, nil),
|
1198
1021
|
"Garbage",
|
1199
1022
|
count_by_identifier[nil],
|
1200
|
-
100.0 * (count_by_identifier[nil].to_f / page.size
|
1023
|
+
100.0 * (count_by_identifier[nil].to_f / page.size),
|
1201
1024
|
]
|
1202
|
-
free_space = page.size - count_by_identifier.inject(0) { |sum,(
|
1025
|
+
free_space = page.size - count_by_identifier.inject(0) { |sum, (_k, v)| sum + v }
|
1203
1026
|
puts " %s %-30s %8i %7.2f%%" % [
|
1204
|
-
" ",
|
1205
|
-
"Free",
|
1027
|
+
unknown_page_content ? "▞" : " ",
|
1028
|
+
unknown_page_content ? "Unknown (no data dictionary)" : "Free",
|
1206
1029
|
free_space,
|
1207
|
-
100.0 * (free_space.to_f / page.size
|
1030
|
+
100.0 * (free_space.to_f / page.size),
|
1208
1031
|
]
|
1209
1032
|
|
1033
|
+
if unknown_page_content
|
1034
|
+
puts
|
1035
|
+
puts "Note:"
|
1036
|
+
puts " Records could not be parsed because no data dictionary or record describer"
|
1037
|
+
puts " was available. Use -s instead of -f, or provide a record describer class."
|
1038
|
+
end
|
1039
|
+
|
1210
1040
|
puts
|
1211
1041
|
end
|
1212
1042
|
|
1213
1043
|
def record_dump(page, record_offset)
|
1214
|
-
|
1215
|
-
|
1216
|
-
end
|
1044
|
+
record = page.record(record_offset)
|
1045
|
+
raise "Record at offset #{record_offset} not found" unless record
|
1217
1046
|
|
1218
1047
|
record.dump
|
1219
1048
|
end
|
1220
1049
|
|
1221
1050
|
def record_history(page, record_offset)
|
1222
|
-
unless page.leaf?
|
1223
|
-
raise "Record is not located on a leaf page; no history available"
|
1224
|
-
end
|
1051
|
+
raise "Record is not located on a leaf page; no history available" unless page.leaf?
|
1225
1052
|
|
1226
|
-
|
1227
|
-
|
1228
|
-
end
|
1053
|
+
record = page.record(record_offset)
|
1054
|
+
raise "Record at offset #{record_offset} not found" unless record
|
1229
1055
|
|
1230
1056
|
puts "%-14s%-20s%s" % [
|
1231
1057
|
"Transaction",
|
@@ -1243,29 +1069,23 @@ def record_history(page, record_offset)
|
|
1243
1069
|
end
|
1244
1070
|
|
1245
1071
|
def index_fseg_lists(index, fseg_name)
|
1246
|
-
unless index.fseg(fseg_name)
|
1247
|
-
raise "File segment '#{fseg_name}' doesn't exist"
|
1248
|
-
end
|
1072
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
|
1249
1073
|
|
1250
1074
|
print_lists(index.each_fseg_list(index.fseg(fseg_name)))
|
1251
1075
|
end
|
1252
1076
|
|
1253
1077
|
def index_fseg_list_iterate(index, fseg_name, list_name)
|
1254
|
-
|
1255
|
-
|
1256
|
-
end
|
1078
|
+
fseg = index.fseg(fseg_name)
|
1079
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless fseg
|
1257
1080
|
|
1258
|
-
|
1259
|
-
|
1260
|
-
end
|
1081
|
+
list = fseg.list(list_name)
|
1082
|
+
raise "List '#{list_name}' doesn't exist" unless list
|
1261
1083
|
|
1262
1084
|
print_xdes_list(index.space, list)
|
1263
1085
|
end
|
1264
1086
|
|
1265
1087
|
def index_fseg_frag_pages(index, fseg_name)
|
1266
|
-
unless index.fseg(fseg_name)
|
1267
|
-
raise "File segment '#{fseg_name}' doesn't exist"
|
1268
|
-
end
|
1088
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
|
1269
1089
|
|
1270
1090
|
print_index_page_summary(index.each_fseg_frag_page(index.fseg(fseg_name)))
|
1271
1091
|
end
|
@@ -1280,17 +1100,17 @@ def index_recurse(index)
|
|
1280
1100
|
page.records,
|
1281
1101
|
page.record_space,
|
1282
1102
|
]
|
1283
|
-
if page.level
|
1103
|
+
if page.level.zero?
|
1284
1104
|
page.each_record do |record|
|
1285
1105
|
puts "%sRECORD: (%s) → (%s)" % [
|
1286
|
-
" " * (depth+1),
|
1106
|
+
" " * (depth + 1),
|
1287
1107
|
record.key_string,
|
1288
1108
|
record.row_string,
|
1289
1109
|
]
|
1290
1110
|
end
|
1291
1111
|
end
|
1292
1112
|
end,
|
1293
|
-
lambda do |
|
1113
|
+
lambda do |_parent_page, child_page, child_min_key, depth|
|
1294
1114
|
puts "%sNODE POINTER RECORD ≥ (%s) → #%i" % [
|
1295
1115
|
" " * depth,
|
1296
1116
|
child_min_key.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }.join(", "),
|
@@ -1301,13 +1121,13 @@ def index_recurse(index)
|
|
1301
1121
|
end
|
1302
1122
|
|
1303
1123
|
def index_record_offsets(index)
|
1304
|
-
puts "%-20s%-20s" % [
|
1305
|
-
|
1306
|
-
|
1124
|
+
puts "%-20s%-20s" % %w[
|
1125
|
+
page_offset
|
1126
|
+
record_offset
|
1307
1127
|
]
|
1308
1128
|
index.recurse(
|
1309
|
-
lambda do |page,
|
1310
|
-
if page.level
|
1129
|
+
lambda do |page, _depth|
|
1130
|
+
if page.level.zero?
|
1311
1131
|
page.each_record do |record|
|
1312
1132
|
puts "%-20i%-20i" % [
|
1313
1133
|
page.offset,
|
@@ -1316,7 +1136,7 @@ def index_record_offsets(index)
|
|
1316
1136
|
end
|
1317
1137
|
end
|
1318
1138
|
end,
|
1319
|
-
|
1139
|
+
->(*_) {}
|
1320
1140
|
)
|
1321
1141
|
end
|
1322
1142
|
|
@@ -1336,13 +1156,13 @@ def index_digraph(index)
|
|
1336
1156
|
child_key.join(", "),
|
1337
1157
|
]
|
1338
1158
|
end
|
1339
|
-
puts " %spage_%i [ shape =
|
1159
|
+
puts " %spage_%i [ shape = 'record'; label = '%s'; ];" % [
|
1340
1160
|
" " * depth,
|
1341
1161
|
page.offset,
|
1342
1162
|
label,
|
1343
1163
|
]
|
1344
1164
|
end,
|
1345
|
-
lambda do |parent_page, child_page,
|
1165
|
+
lambda do |parent_page, child_page, _child_key, depth|
|
1346
1166
|
puts " %spage_%i:dir_%i → page_%i:page:nw;" % [
|
1347
1167
|
" " * depth,
|
1348
1168
|
parent_page.offset,
|
@@ -1355,14 +1175,14 @@ def index_digraph(index)
|
|
1355
1175
|
end
|
1356
1176
|
|
1357
1177
|
def index_level_summary(index, level)
|
1358
|
-
puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % [
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
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
|
1366
1186
|
]
|
1367
1187
|
|
1368
1188
|
index.each_page_at_level(level) do |page|
|
@@ -1379,15 +1199,14 @@ def index_level_summary(index, level)
|
|
1379
1199
|
end
|
1380
1200
|
|
1381
1201
|
def undo_history_summary(innodb_system)
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
"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
|
1391
1210
|
]
|
1392
1211
|
|
1393
1212
|
history_list.each do |history|
|
@@ -1417,6 +1236,7 @@ def usage(exit_code, message = nil)
|
|
1417
1236
|
exit exit_code
|
1418
1237
|
end
|
1419
1238
|
|
1239
|
+
# rubocop:disable Layout/HeredocIndentation
|
1420
1240
|
print <<'END_OF_USAGE'
|
1421
1241
|
|
1422
1242
|
Usage: innodb_space <options> <mode>
|
@@ -1444,9 +1264,9 @@ The following options are supported:
|
|
1444
1264
|
|
1445
1265
|
--system-space-file, -s <arg>
|
1446
1266
|
Load the system tablespace file or files <arg>: Either a single file e.g.
|
1447
|
-
|
1267
|
+
'ibdata1', a comma-delimited list of files e.g. 'ibdata1,ibdata1', or a
|
1448
1268
|
directory name. If a directory name is provided, it will be scanned for all
|
1449
|
-
files named
|
1269
|
+
files named 'ibdata?' which will then be sorted alphabetically and used to
|
1450
1270
|
load the system tablespace.
|
1451
1271
|
|
1452
1272
|
--table-name, -T <name>
|
@@ -1467,8 +1287,11 @@ The following options are supported:
|
|
1467
1287
|
--list, -L <list>
|
1468
1288
|
Operate on the list <list>.
|
1469
1289
|
|
1290
|
+
--fseg-id, -F <fseg_id>
|
1291
|
+
Operate on the file segment (fseg) <fseg_id>.
|
1292
|
+
|
1470
1293
|
--require, -r <file>
|
1471
|
-
Use Ruby's
|
1294
|
+
Use Ruby's 'require' to load the file <file>. This is useful for loading
|
1472
1295
|
classes with record describers.
|
1473
1296
|
|
1474
1297
|
--describer, -d <describer>
|
@@ -1496,14 +1319,18 @@ The following modes are supported:
|
|
1496
1319
|
provided with the --page/-p argument.
|
1497
1320
|
|
1498
1321
|
space-index-pages-summary
|
1499
|
-
Summarize all
|
1500
|
-
page fill rates and record counts per page. In addition to
|
1501
|
-
|
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.
|
1502
1325
|
A starting page number can be provided with the --page/-p argument.
|
1503
1326
|
|
1327
|
+
space-index-fseg-pages-summary
|
1328
|
+
The same as space-index-pages-summary but only iterate one fseg, provided
|
1329
|
+
with the --fseg-id/-F argument.
|
1330
|
+
|
1504
1331
|
space-index-pages-free-plot
|
1505
1332
|
Use Ruby's gnuplot module to produce a scatterplot of page free space for
|
1506
|
-
all
|
1333
|
+
all 'INDEX' and 'ALLOCATED' pages in a tablespace. More aesthetically
|
1507
1334
|
pleasing plots can be produced with space-index-pages-summary output,
|
1508
1335
|
but this is a quick and easy way to produce a passable plot. A starting
|
1509
1336
|
page number can be provided with the --page/-p argument.
|
@@ -1535,19 +1362,13 @@ The following modes are supported:
|
|
1535
1362
|
color and Unicode box drawing characters to show page usage throughout
|
1536
1363
|
the space.
|
1537
1364
|
|
1538
|
-
space-extents-illustrate-svg
|
1539
|
-
Iterate through all extents, illustrating the extent usage in SVG format
|
1540
|
-
printed to stdout to show page usage throughout the space.
|
1541
|
-
|
1542
1365
|
space-lsn-age-illustrate
|
1543
1366
|
Iterate through all pages, producing a heat map colored by the page LSN
|
1544
1367
|
using ANSI color and Unicode box drawing characters, allowing the user to
|
1545
1368
|
get an overview of page modification recency.
|
1546
1369
|
|
1547
|
-
space-
|
1548
|
-
Iterate through all
|
1549
|
-
producing SVG format output, allowing the user to get an overview of page
|
1550
|
-
modification recency.
|
1370
|
+
space-inodes-fseg-id
|
1371
|
+
Iterate through all inodes, printing only the FSEG ID.
|
1551
1372
|
|
1552
1373
|
space-inodes-summary
|
1553
1374
|
Iterate through all inodes, printing a short summary of each FSEG.
|
@@ -1583,7 +1404,7 @@ The following modes are supported:
|
|
1583
1404
|
Iterate the file segment list (whose name is provided in the first --list/-L
|
1584
1405
|
argument) for internal or leaf pages for a given index (whose root page
|
1585
1406
|
is provided in the first --page/-p argument). The lists used for each
|
1586
|
-
index are
|
1407
|
+
index are 'full', 'not_full', and 'free'.
|
1587
1408
|
|
1588
1409
|
index-fseg-internal-frag-pages
|
1589
1410
|
index-fseg-leaf-frag-pages
|
@@ -1591,11 +1412,14 @@ The following modes are supported:
|
|
1591
1412
|
page must be provided with --page/-p.
|
1592
1413
|
|
1593
1414
|
page-dump
|
1594
|
-
Dump the contents of a page, using the Ruby pp (
|
1415
|
+
Dump the contents of a page, using the Ruby pp ('pretty-print') module.
|
1595
1416
|
|
1596
1417
|
page-account
|
1597
1418
|
Account for a page's usage in FSEGs.
|
1598
1419
|
|
1420
|
+
page-validate
|
1421
|
+
Validate the contents of a page.
|
1422
|
+
|
1599
1423
|
page-directory-summary
|
1600
1424
|
Summarize the record contents of the page directory in a page. If a record
|
1601
1425
|
describer is available, the key of each record will be printed.
|
@@ -1622,6 +1446,7 @@ The following modes are supported:
|
|
1622
1446
|
A record offset must be provided with -R/--record.
|
1623
1447
|
|
1624
1448
|
END_OF_USAGE
|
1449
|
+
# rubocop:enable Layout/HeredocIndentation
|
1625
1450
|
|
1626
1451
|
exit exit_code
|
1627
1452
|
end
|
@@ -1630,31 +1455,39 @@ Signal.trap("INT") { exit }
|
|
1630
1455
|
Signal.trap("PIPE") { exit }
|
1631
1456
|
|
1632
1457
|
@options = OpenStruct.new
|
1633
|
-
@options.trace
|
1634
|
-
@options.system_space_file
|
1635
|
-
@options.space_file
|
1636
|
-
@options.table_name
|
1637
|
-
@options.index_name
|
1638
|
-
@options.page
|
1639
|
-
@options.record
|
1640
|
-
@options.level
|
1641
|
-
@options.list
|
1642
|
-
@options.
|
1643
|
-
|
1458
|
+
@options.trace = 0
|
1459
|
+
@options.system_space_file = nil
|
1460
|
+
@options.space_file = nil
|
1461
|
+
@options.table_name = nil
|
1462
|
+
@options.index_name = nil
|
1463
|
+
@options.page = nil
|
1464
|
+
@options.record = nil
|
1465
|
+
@options.level = nil
|
1466
|
+
@options.list = nil
|
1467
|
+
@options.fseg_id = nil
|
1468
|
+
@options.describer = nil
|
1469
|
+
@options.illustration_line_width = 64
|
1470
|
+
@options.illustration_block_size = 8
|
1471
|
+
|
1472
|
+
# rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
|
1644
1473
|
getopt_options = [
|
1645
|
-
[ "--help",
|
1646
|
-
[ "--trace",
|
1647
|
-
[ "--system-space-file",
|
1648
|
-
[ "--space-file",
|
1649
|
-
[ "--table-name",
|
1650
|
-
[ "--index-name",
|
1651
|
-
[ "--page",
|
1652
|
-
[ "--record",
|
1653
|
-
[ "--level",
|
1654
|
-
[ "--list",
|
1655
|
-
[ "--
|
1656
|
-
[ "--
|
1474
|
+
[ "--help", "-?", GetoptLong::NO_ARGUMENT ],
|
1475
|
+
[ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
|
1476
|
+
[ "--system-space-file", "-s", GetoptLong::REQUIRED_ARGUMENT ],
|
1477
|
+
[ "--space-file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
|
1478
|
+
[ "--table-name", "-T", GetoptLong::REQUIRED_ARGUMENT ],
|
1479
|
+
[ "--index-name", "-I", GetoptLong::REQUIRED_ARGUMENT ],
|
1480
|
+
[ "--page", "-p", GetoptLong::REQUIRED_ARGUMENT ],
|
1481
|
+
[ "--record", "-R", GetoptLong::REQUIRED_ARGUMENT ],
|
1482
|
+
[ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
|
1483
|
+
[ "--list", "-L", GetoptLong::REQUIRED_ARGUMENT ],
|
1484
|
+
[ "--fseg-id", "-F", GetoptLong::REQUIRED_ARGUMENT ],
|
1485
|
+
[ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
1486
|
+
[ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
|
1487
|
+
[ "--illustration-line-width", GetoptLong::REQUIRED_ARGUMENT ],
|
1488
|
+
[ "--illustration-block-size", GetoptLong::REQUIRED_ARGUMENT ],
|
1657
1489
|
]
|
1490
|
+
# rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
|
1658
1491
|
|
1659
1492
|
getopt = GetoptLong.new(*getopt_options)
|
1660
1493
|
|
@@ -1680,24 +1513,30 @@ getopt.each do |opt, arg|
|
|
1680
1513
|
@options.level = arg.to_i
|
1681
1514
|
when "--list"
|
1682
1515
|
@options.list = arg.to_sym
|
1516
|
+
when "--fseg-id"
|
1517
|
+
@options.fseg_id = arg.to_i
|
1683
1518
|
when "--require"
|
1684
1519
|
require File.expand_path(arg)
|
1685
1520
|
when "--describer"
|
1686
1521
|
@options.describer = arg
|
1522
|
+
when "--illustration-line-width"
|
1523
|
+
@options.illustration_line_width = arg.to_i
|
1524
|
+
when "--illustration-block-size"
|
1525
|
+
@options.illustration_block_size = arg.to_i
|
1687
1526
|
end
|
1688
1527
|
end
|
1689
1528
|
|
1690
|
-
|
1529
|
+
# rubocop:disable Style/IfUnlessModifier
|
1530
|
+
|
1531
|
+
unless @options.system_space_file || @options.space_file
|
1691
1532
|
usage 1, "System space file (-s) or space file (-f) must be specified"
|
1692
1533
|
end
|
1693
1534
|
|
1694
|
-
if @options.system_space_file
|
1535
|
+
if @options.system_space_file && @options.space_file
|
1695
1536
|
usage 1, "Only one of system space or space file may be specified"
|
1696
1537
|
end
|
1697
1538
|
|
1698
|
-
if @options.trace > 1
|
1699
|
-
BufferCursor.trace!
|
1700
|
-
end
|
1539
|
+
BufferCursor.trace! if @options.trace > 1
|
1701
1540
|
|
1702
1541
|
# A few globals that we'll try to populate from the command-line arguments.
|
1703
1542
|
innodb_system = nil
|
@@ -1709,7 +1548,7 @@ if @options.system_space_file
|
|
1709
1548
|
innodb_system = Innodb::System.new(@options.system_space_file)
|
1710
1549
|
end
|
1711
1550
|
|
1712
|
-
if innodb_system
|
1551
|
+
if innodb_system && @options.table_name
|
1713
1552
|
table_tablespace = innodb_system.space_by_table_name(@options.table_name)
|
1714
1553
|
space = table_tablespace || innodb_system.system_space
|
1715
1554
|
elsif @options.space_file
|
@@ -1719,24 +1558,17 @@ else
|
|
1719
1558
|
end
|
1720
1559
|
|
1721
1560
|
if @options.describer
|
1722
|
-
describer = eval(@options.describer)
|
1723
|
-
|
1724
|
-
describer = Innodb::RecordDescriber.const_get(@options.describer)
|
1725
|
-
end
|
1561
|
+
describer = eval(@options.describer) # rubocop:disable Security/Eval
|
1562
|
+
describer ||= Innodb::RecordDescriber.const_get(@options.describer)
|
1726
1563
|
space.record_describer = describer.new
|
1727
1564
|
end
|
1728
1565
|
|
1729
|
-
if innodb_system
|
1566
|
+
if innodb_system && @options.table_name && @options.index_name
|
1730
1567
|
index = innodb_system.index_by_name(@options.table_name, @options.index_name)
|
1731
|
-
|
1732
|
-
page = space.page(@options.page)
|
1733
|
-
else
|
1734
|
-
page = index.root
|
1735
|
-
end
|
1568
|
+
page = @options.page ? space.page(@options.page) : index.root
|
1736
1569
|
elsif @options.page
|
1737
|
-
|
1738
|
-
|
1739
|
-
end
|
1570
|
+
page = space.page(@options.page)
|
1571
|
+
index = space.index(@options.page) if page&.type == :INDEX && page&.root?
|
1740
1572
|
end
|
1741
1573
|
|
1742
1574
|
# The non-option argument on the command line is the mode (usually the last,
|
@@ -1744,50 +1576,64 @@ end
|
|
1744
1576
|
mode = ARGV.shift
|
1745
1577
|
|
1746
1578
|
unless mode
|
1747
|
-
usage
|
1579
|
+
usage(1, "At least one mode must be provided")
|
1748
1580
|
end
|
1749
1581
|
|
1750
|
-
if /^(system-|data-dictionary-)/.match(mode)
|
1751
|
-
usage
|
1582
|
+
if /^(system-|data-dictionary-)/.match(mode) && !innodb_system
|
1583
|
+
usage(1, "System tablespace must be specified using -s/--system-space-file")
|
1752
1584
|
end
|
1753
1585
|
|
1754
|
-
if /^space-/.match(mode)
|
1755
|
-
usage
|
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
|
+
)
|
1756
1592
|
end
|
1757
1593
|
|
1758
|
-
if /^index-/.match(mode)
|
1759
|
-
usage
|
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
|
+
)
|
1760
1600
|
end
|
1761
1601
|
|
1762
|
-
if /^page-/.match(mode)
|
1763
|
-
usage
|
1602
|
+
if /^page-/.match(mode) && !page
|
1603
|
+
usage(1, "Page number must be specified using -p/--page")
|
1764
1604
|
end
|
1765
1605
|
|
1766
|
-
if /^record-/.match(mode)
|
1767
|
-
usage
|
1606
|
+
if /^record-/.match(mode) && !@options.record
|
1607
|
+
usage(1, "Record offset must be specified using -R/--record")
|
1768
1608
|
end
|
1769
1609
|
|
1770
|
-
if /-list-iterate$/.match(mode)
|
1771
|
-
usage
|
1610
|
+
if /-list-iterate$/.match(mode) && !@options.list
|
1611
|
+
usage(1, "List name must be specified using -L/--list")
|
1772
1612
|
end
|
1773
1613
|
|
1774
|
-
if /-level-/.match(mode)
|
1775
|
-
usage
|
1614
|
+
if /-level-/.match(mode) && !@options.level
|
1615
|
+
usage(1, "Level must be specified using -l/--level")
|
1776
1616
|
end
|
1777
1617
|
|
1778
|
-
if [
|
1779
|
-
|
1780
|
-
|
1781
|
-
|
1782
|
-
|
1783
|
-
].include?(mode)
|
1784
|
-
usage
|
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")
|
1785
1625
|
end
|
1786
1626
|
|
1787
|
-
if
|
1788
|
-
|
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")
|
1789
1631
|
end
|
1790
1632
|
|
1633
|
+
# rubocop:enable Style/IfUnlessModifier
|
1634
|
+
|
1635
|
+
BufferCursor.trace! if @options.trace.positive?
|
1636
|
+
|
1791
1637
|
case mode
|
1792
1638
|
when "system-spaces"
|
1793
1639
|
system_spaces(innodb_system)
|
@@ -1803,9 +1649,10 @@ when "space-summary"
|
|
1803
1649
|
space_summary(space, @options.page || 0)
|
1804
1650
|
when "space-index-pages-summary"
|
1805
1651
|
space_index_pages_summary(space, @options.page || 0)
|
1652
|
+
when "space-index-fseg-pages-summary"
|
1653
|
+
space_index_fseg_pages_summary(space, @options.fseg_id)
|
1806
1654
|
when "space-index-pages-free-plot"
|
1807
|
-
|
1808
|
-
space_index_pages_free_plot(space, name, @options.page || 0)
|
1655
|
+
space_index_pages_free_plot(space, @options.page || 0)
|
1809
1656
|
when "space-page-type-regions"
|
1810
1657
|
space_page_type_regions(space, @options.page || 0)
|
1811
1658
|
when "space-page-type-summary"
|
@@ -1820,12 +1667,10 @@ when "space-extents"
|
|
1820
1667
|
space_extents(space)
|
1821
1668
|
when "space-extents-illustrate"
|
1822
1669
|
space_extents_illustrate(space)
|
1823
|
-
when "space-extents-illustrate-svg"
|
1824
|
-
space_extents_illustrate_svg(space)
|
1825
1670
|
when "space-lsn-age-illustrate"
|
1826
1671
|
space_lsn_age_illustrate(space)
|
1827
|
-
when "space-
|
1828
|
-
|
1672
|
+
when "space-inodes-fseg-id"
|
1673
|
+
space_inodes_fseg_id(space)
|
1829
1674
|
when "space-inodes-summary"
|
1830
1675
|
space_inodes_summary(space)
|
1831
1676
|
when "space-inodes-detail"
|
@@ -1854,6 +1699,8 @@ when "page-dump"
|
|
1854
1699
|
page.dump
|
1855
1700
|
when "page-account"
|
1856
1701
|
page_account(innodb_system, space, @options.page)
|
1702
|
+
when "page-validate"
|
1703
|
+
page_validate(innodb_system, space, @options.page)
|
1857
1704
|
when "page-directory-summary"
|
1858
1705
|
page_directory_summary(space, page)
|
1859
1706
|
when "page-records"
|