innodb_ruby 0.9.14 → 0.12.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 +13 -18
- data/bin/innodb_space +654 -778
- 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 -276
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +196 -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/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/page.rb +417 -414
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +381 -413
- 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
- data/lib/innodb.rb +4 -5
- metadata +100 -25
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c2dd793281e3b83403e55c8d4e9104898d5df6099af80dd4453795a524e3fb32
|
4
|
+
data.tar.gz: d6ac28ab11759a618f9a84af611a705644f5f47f9c1cd387360b628bac88f527
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ffb5ba4b9805d4cb78aaecaec92adb4e6064d73a9349fbeb43fcc938b958adeb14f140747dd540f76d31bbf9a38fbde259bd99f3a9cc64f32a1456efc2006997
|
7
|
+
data.tar.gz: 9e98bd61fe4448fdbb15be66975581fe65d97c342685c21f2309ead8b0ed1fc217b08baf2855c336d8ea426e22b7c0556e0926064722e6cc23a95312a6a63ca4
|
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,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)
|
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,27 +1,36 @@
|
|
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
|
|
9
|
+
class String
|
10
|
+
def squish!
|
11
|
+
gsub!(/[[:space:]]+/, " ")
|
12
|
+
strip!
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def squish
|
17
|
+
dup.squish!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
8
21
|
# Convert a floating point RGB array into an ANSI color number approximating it.
|
9
22
|
def rgb_to_ansi(rgb)
|
10
23
|
rgb_n = rgb.map { |c| (c * 5.0).round }
|
11
24
|
16 + (rgb_n[0] * 36) + (rgb_n[1] * 6) + rgb_n[2]
|
12
25
|
end
|
13
26
|
|
14
|
-
def rgb_to_rgbhex(rgb)
|
15
|
-
rgb.map { |c| "%02x" % [(c * 255.0).round] }.join
|
16
|
-
end
|
17
|
-
|
18
27
|
# Interpolate intermediate float-arrays between two float-arrays. Do not
|
19
28
|
# include the points a and b in the result.
|
20
|
-
def interpolate(
|
21
|
-
deltas =
|
22
|
-
steps =
|
29
|
+
def interpolate(ary_a, ary_b, count)
|
30
|
+
deltas = ary_a.each_index.map { |i| ary_b[i] - ary_a[i] }
|
31
|
+
steps = ary_a.each_index.map { |i| deltas[i].to_f / (count.to_f + 1) }
|
23
32
|
|
24
|
-
count.times.to_a.map { |i|
|
33
|
+
count.times.to_a.map { |i| ary_a.each_index.map { |j| ary_a[j] + ((i + 1).to_f * steps[j]) } }
|
25
34
|
end
|
26
35
|
|
27
36
|
# Interpolate intermediate float-arrays between each step in a sequence of
|
@@ -29,7 +38,7 @@ end
|
|
29
38
|
def interpolate_sequence(sequence, count)
|
30
39
|
result = []
|
31
40
|
result << sequence.first
|
32
|
-
(sequence.size-1).times.map { |n| [sequence[n], sequence[n+1]] }.each do |from, to|
|
41
|
+
(sequence.size - 1).times.map { |n| [sequence[n], sequence[n + 1]] }.each do |from, to|
|
33
42
|
interpolate(from, to, count).each do |step|
|
34
43
|
result << step
|
35
44
|
end
|
@@ -48,15 +57,11 @@ HEATMAP_PROGRESSION = [
|
|
48
57
|
[1.0, 1.0, 0.0], # Yellow
|
49
58
|
[1.0, 0.0, 0.0], # Red
|
50
59
|
[1.0, 0.0, 1.0], # Purple
|
51
|
-
]
|
60
|
+
].freeze
|
52
61
|
|
53
62
|
# Typical heatmap color progression.
|
54
63
|
ANSI_COLORS_HEATMAP = interpolate_sequence(HEATMAP_PROGRESSION, 6).map { |rgb| rgb_to_ansi(rgb) }
|
55
64
|
|
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
65
|
# The 24-step grayscale progression.
|
61
66
|
ANSI_COLORS_GRAYSCALE = (0xe8..0xff).to_a
|
62
67
|
|
@@ -66,69 +71,47 @@ def ansi_color(color, text)
|
|
66
71
|
end
|
67
72
|
|
68
73
|
# Zero and 1/8 through 8/8 illustrations.
|
69
|
-
BLOCK_CHARS_V =
|
70
|
-
BLOCK_CHARS_H =
|
74
|
+
BLOCK_CHARS_V = "░▁▂▃▄▅▆▇█".chars.freeze
|
75
|
+
BLOCK_CHARS_H = "░▏▎▍▌▋▊▉█".chars.freeze
|
71
76
|
|
72
77
|
# A reasonably large prime number to multiply identifiers by in order to
|
73
78
|
# space out the colors used for similar identifiers.
|
74
|
-
COLOR_SPACING_PRIME =
|
79
|
+
COLOR_SPACING_PRIME = 999_983
|
75
80
|
|
76
81
|
# Return a string with a possibly colored filled block for use in printing
|
77
82
|
# to an ANSI-capable Unicode-enabled terminal.
|
78
|
-
def filled_block(fraction, identifier=nil, block_chars=BLOCK_CHARS_V)
|
79
|
-
if fraction
|
83
|
+
def filled_block(fraction, identifier = nil, block_chars = BLOCK_CHARS_V)
|
84
|
+
if fraction.zero?
|
80
85
|
block = block_chars[0]
|
81
86
|
else
|
82
|
-
parts = (fraction.to_f * (block_chars.size.to_f-1)).floor
|
83
|
-
block = block_chars[[block_chars.size-1, parts+1].min]
|
87
|
+
parts = (fraction.to_f * (block_chars.size.to_f - 1)).floor
|
88
|
+
block = block_chars[[block_chars.size - 1, parts + 1].min]
|
84
89
|
end
|
85
90
|
if identifier
|
86
91
|
# ANSI 256-color mode, color palette starts at 10 and contains 216 colors.
|
87
|
-
color = 16 + ((identifier * COLOR_SPACING_PRIME)
|
92
|
+
color = 16 + ((identifier * COLOR_SPACING_PRIME) % 216)
|
88
93
|
ansi_color(color, block)
|
89
94
|
else
|
90
95
|
block
|
91
96
|
end
|
92
97
|
end
|
93
98
|
|
94
|
-
def
|
95
|
-
|
96
|
-
end
|
99
|
+
def center(text, width)
|
100
|
+
return text if text.size >= width
|
97
101
|
|
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(" ")
|
102
|
+
spaces = (width - text.size) / 2
|
103
|
+
(" " * spaces) + text + (" " * spaces)
|
121
104
|
end
|
122
105
|
|
123
106
|
# Print metadata about each list in an array of InnoDB::List objects.
|
124
107
|
def print_lists(lists)
|
125
|
-
puts "%-20s%-12s%-12s%-12s%-12s%-12s" % [
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
108
|
+
puts "%-20s%-12s%-12s%-12s%-12s%-12s" % %w[
|
109
|
+
name
|
110
|
+
length
|
111
|
+
f_page
|
112
|
+
f_offset
|
113
|
+
l_page
|
114
|
+
l_offset
|
132
115
|
]
|
133
116
|
|
134
117
|
lists.each do |name, list|
|
@@ -146,33 +129,31 @@ end
|
|
146
129
|
# Print a page usage bitmap for each extent descriptor in an array of
|
147
130
|
# Innodb::XdesEntry objects.
|
148
131
|
def print_xdes_list(space, list)
|
149
|
-
puts "%-12s%-64s" % [
|
150
|
-
|
151
|
-
|
132
|
+
puts "%-12s%-64s" % %w[
|
133
|
+
start_page
|
134
|
+
page_used_bitmap
|
152
135
|
]
|
153
136
|
|
154
137
|
list.each do |entry|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
},
|
163
|
-
]
|
138
|
+
display = entry.each_page_status.inject("") do |bitmap, (page_number, page_status)|
|
139
|
+
if page_number < space.pages
|
140
|
+
bitmap += page_status[:free] ? "." : "#"
|
141
|
+
end
|
142
|
+
bitmap
|
143
|
+
end
|
144
|
+
puts "%-12i%-64s" % [entry.xdes[:start_page], display]
|
164
145
|
end
|
165
146
|
end
|
166
147
|
|
167
148
|
# Print a summary of page usage for all pages in an index.
|
168
149
|
def print_index_page_summary(pages)
|
169
|
-
puts "%-12s%-8s%-8s%-8s%-8s%-8s" % [
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
150
|
+
puts "%-12s%-8s%-8s%-8s%-8s%-8s" % %w[
|
151
|
+
page
|
152
|
+
index
|
153
|
+
level
|
154
|
+
data
|
155
|
+
free
|
156
|
+
records
|
176
157
|
]
|
177
158
|
|
178
159
|
pages.each do |page_number, page|
|
@@ -187,17 +168,17 @@ def print_index_page_summary(pages)
|
|
187
168
|
page.records,
|
188
169
|
]
|
189
170
|
when :ALLOCATED
|
190
|
-
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [
|
171
|
+
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [page_number, 0, 0, 0, page.size, 0]
|
191
172
|
end
|
192
173
|
end
|
193
174
|
end
|
194
175
|
|
195
176
|
# Print a summary of all spaces in the InnoDB system.
|
196
177
|
def system_spaces(innodb_system)
|
197
|
-
puts "%-32s%-12s%-12s" % [
|
198
|
-
|
199
|
-
|
200
|
-
|
178
|
+
puts "%-32s%-12s%-12s" % %w[
|
179
|
+
name
|
180
|
+
pages
|
181
|
+
indexes
|
201
182
|
]
|
202
183
|
|
203
184
|
print_space_information = lambda do |name, space|
|
@@ -213,6 +194,7 @@ def system_spaces(innodb_system)
|
|
213
194
|
innodb_system.each_table_name do |table_name|
|
214
195
|
space = innodb_system.space_by_table_name(table_name)
|
215
196
|
next unless space
|
197
|
+
|
216
198
|
print_space_information.call(table_name, space)
|
217
199
|
end
|
218
200
|
|
@@ -223,15 +205,15 @@ end
|
|
223
205
|
|
224
206
|
# Print the contents of the SYS_TABLES data dictionary table.
|
225
207
|
def data_dictionary_tables(innodb_system)
|
226
|
-
puts "%-32s%-12s%-12s%-12s%-12s%-12s%-15s%-12s" % [
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
208
|
+
puts "%-32s%-12s%-12s%-12s%-12s%-12s%-15s%-12s" % %w[
|
209
|
+
name
|
210
|
+
id
|
211
|
+
n_cols
|
212
|
+
type
|
213
|
+
mix_id
|
214
|
+
mix_len
|
215
|
+
cluster_name
|
216
|
+
space
|
235
217
|
]
|
236
218
|
|
237
219
|
innodb_system.data_dictionary.each_table do |record|
|
@@ -250,14 +232,14 @@ end
|
|
250
232
|
|
251
233
|
# Print the contents of the SYS_COLUMNS data dictionary table.
|
252
234
|
def data_dictionary_columns(innodb_system)
|
253
|
-
puts "%-12s%-6s%-32s%-12s%-12s%-6s%-6s" % [
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
235
|
+
puts "%-12s%-6s%-32s%-12s%-12s%-6s%-6s" % %w[
|
236
|
+
table_id
|
237
|
+
pos
|
238
|
+
name
|
239
|
+
mtype
|
240
|
+
prtype
|
241
|
+
len
|
242
|
+
prec
|
261
243
|
]
|
262
244
|
|
263
245
|
innodb_system.data_dictionary.each_column do |record|
|
@@ -275,14 +257,14 @@ end
|
|
275
257
|
|
276
258
|
# Print the contents of the SYS_INDEXES data dictionary table.
|
277
259
|
def data_dictionary_indexes(innodb_system)
|
278
|
-
puts "%-12s%-12s%-32s%-10s%-6s%-12s%-12s" % [
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
260
|
+
puts "%-12s%-12s%-32s%-10s%-6s%-12s%-12s" % %w[
|
261
|
+
table_id
|
262
|
+
id
|
263
|
+
name
|
264
|
+
n_fields
|
265
|
+
type
|
266
|
+
space
|
267
|
+
page_no
|
286
268
|
]
|
287
269
|
|
288
270
|
innodb_system.data_dictionary.each_index do |record|
|
@@ -300,10 +282,10 @@ end
|
|
300
282
|
|
301
283
|
# Print the contents of the SYS_FIELDS data dictionary table.
|
302
284
|
def data_dictionary_fields(innodb_system)
|
303
|
-
puts "%-12s%-12s%-32s" % [
|
304
|
-
|
305
|
-
|
306
|
-
|
285
|
+
puts "%-12s%-12s%-32s" % %w[
|
286
|
+
index_id
|
287
|
+
pos
|
288
|
+
col_name
|
307
289
|
]
|
308
290
|
|
309
291
|
innodb_system.data_dictionary.each_field do |record|
|
@@ -316,12 +298,12 @@ def data_dictionary_fields(innodb_system)
|
|
316
298
|
end
|
317
299
|
|
318
300
|
def space_summary(space, start_page)
|
319
|
-
puts "%-12s%-20s%-12s%-12s%-20s" % [
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
301
|
+
puts "%-12s%-20s%-12s%-12s%-20s" % %w[
|
302
|
+
page
|
303
|
+
type
|
304
|
+
prev
|
305
|
+
next
|
306
|
+
lsn
|
325
307
|
]
|
326
308
|
|
327
309
|
space.each_page(start_page) do |page_number, page|
|
@@ -339,12 +321,16 @@ def space_index_pages_summary(space, start_page)
|
|
339
321
|
print_index_page_summary(space.each_page(start_page))
|
340
322
|
end
|
341
323
|
|
324
|
+
def space_index_fseg_pages_summary(space, fseg_id)
|
325
|
+
print_index_page_summary(space.inode(fseg_id).each_page)
|
326
|
+
end
|
327
|
+
|
342
328
|
def space_page_type_regions(space, start_page)
|
343
|
-
puts "%-12s%-12s%-12s%-20s" % [
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
329
|
+
puts "%-12s%-12s%-12s%-20s" % %w[
|
330
|
+
start
|
331
|
+
end
|
332
|
+
count
|
333
|
+
type
|
348
334
|
]
|
349
335
|
|
350
336
|
space.each_page_type_region(start_page) do |region|
|
@@ -363,16 +349,16 @@ def space_page_type_summary(space, start_page)
|
|
363
349
|
page_count = 0
|
364
350
|
# A Hash of page type => count.
|
365
351
|
page_type = Hash.new(0)
|
366
|
-
space.each_page(start_page) do |
|
352
|
+
space.each_page(start_page) do |_page_number, page|
|
367
353
|
page_count += 1
|
368
354
|
page_type[page.type] += 1
|
369
355
|
end
|
370
356
|
|
371
|
-
puts "%-20s%-12s%-12s%-20s" % [
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
357
|
+
puts "%-20s%-12s%-12s%-20s" % %w[
|
358
|
+
type
|
359
|
+
count
|
360
|
+
percent
|
361
|
+
description
|
376
362
|
]
|
377
363
|
|
378
364
|
# Sort the page type Hash by count, descending.
|
@@ -380,7 +366,7 @@ def space_page_type_summary(space, start_page)
|
|
380
366
|
puts "%-20s%-12i%-12.2f%-20s" % [
|
381
367
|
type,
|
382
368
|
type_count,
|
383
|
-
100.0 * (type_count.to_f / page_count
|
369
|
+
100.0 * (type_count.to_f / page_count),
|
384
370
|
Innodb::Page::PAGE_TYPE[type][:description],
|
385
371
|
]
|
386
372
|
end
|
@@ -393,16 +379,14 @@ end
|
|
393
379
|
def space_list_iterate(space, list_name)
|
394
380
|
fsp = space.page(0).fsp_header
|
395
381
|
|
396
|
-
|
397
|
-
raise "List '#{list_name}' doesn't exist"
|
398
|
-
end
|
382
|
+
raise "List '#{list_name}' doesn't exist" unless fsp[list_name].is_a?(Innodb::List)
|
399
383
|
|
400
384
|
case fsp[list_name]
|
401
385
|
when Innodb::List::Xdes
|
402
386
|
print_xdes_list(space, fsp[list_name])
|
403
387
|
when Innodb::List::Inode
|
404
|
-
puts "%-12s" % [
|
405
|
-
|
388
|
+
puts "%-12s" % %w[
|
389
|
+
page
|
406
390
|
]
|
407
391
|
fsp[list_name].each do |page|
|
408
392
|
puts "%-12i" % [
|
@@ -413,23 +397,25 @@ def space_list_iterate(space, list_name)
|
|
413
397
|
end
|
414
398
|
|
415
399
|
def space_indexes(innodb_system, space)
|
416
|
-
puts "%-12s%-32s%-12s%-12s%-12s%-12s%-12s" % [
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
400
|
+
puts "%-12s%-32s%-12s%-12s%-12s%-12s%-12s%-12s" % %w[
|
401
|
+
id
|
402
|
+
name
|
403
|
+
root
|
404
|
+
fseg
|
405
|
+
fseg_id
|
406
|
+
used
|
407
|
+
allocated
|
408
|
+
fill_factor
|
424
409
|
]
|
425
410
|
|
426
411
|
space.each_index do |index|
|
427
412
|
index.each_fseg do |fseg_name, fseg|
|
428
|
-
puts "%-12i%-32s%-12i%-12s%-12i%-12i%-12s" % [
|
413
|
+
puts "%-12i%-32s%-12i%-12s%-12i%-12i%-12i%-12s" % [
|
429
414
|
index.id,
|
430
415
|
innodb_system ? innodb_system.index_name_by_id(index.id) : "",
|
431
416
|
index.root.offset,
|
432
417
|
fseg_name,
|
418
|
+
fseg.fseg_id,
|
433
419
|
fseg.used_pages,
|
434
420
|
fseg.total_pages,
|
435
421
|
"%.2f%%" % fseg.fill_factor,
|
@@ -438,17 +424,15 @@ def space_indexes(innodb_system, space)
|
|
438
424
|
end
|
439
425
|
end
|
440
426
|
|
441
|
-
def space_index_pages_free_plot(space,
|
442
|
-
unless require "gnuplot"
|
443
|
-
raise "Couldn't load gnuplot. Is it installed?"
|
444
|
-
end
|
427
|
+
def space_index_pages_free_plot(space, start_page)
|
428
|
+
raise "Could not load gnuplot. Is it installed?" unless require "gnuplot"
|
445
429
|
|
446
|
-
index_data = {0 => {:
|
430
|
+
index_data = { 0 => { x: [], y: [] } }
|
447
431
|
|
448
432
|
space.each_page(start_page) do |page_number, page|
|
449
433
|
case page.type
|
450
434
|
when :INDEX
|
451
|
-
data = (index_data[page.page_header[:index_id]] ||= {:
|
435
|
+
data = (index_data[page.page_header[:index_id]] ||= { x: [], y: [] })
|
452
436
|
data[:x] << page_number
|
453
437
|
data[:y] << page.free_space
|
454
438
|
when :ALLOCATED
|
@@ -457,15 +441,17 @@ def space_index_pages_free_plot(space, image, start_page)
|
|
457
441
|
end
|
458
442
|
end
|
459
443
|
|
460
|
-
|
444
|
+
image_name = space.name.sub(".ibd", "").gsub(/[^a-zA-Z0-9_]/, "_").sub(/\A_+/, "")
|
445
|
+
image_file = "#{image_name}_free.png"
|
446
|
+
|
461
447
|
# Aim for one horizontal pixel per extent, but min 1k and max 10k width.
|
462
|
-
image_width = [
|
448
|
+
image_width = [10_000, [1_000, space.pages / space.pages_per_extent].max].min
|
463
449
|
|
464
450
|
Gnuplot.open do |gp|
|
465
451
|
Gnuplot::Plot.new(gp) do |plot|
|
466
452
|
plot.terminal "png size #{image_width}, 800"
|
467
453
|
plot.output image_file
|
468
|
-
plot.title
|
454
|
+
plot.title image_name.gsub("_", " ")
|
469
455
|
plot.key "reverse left top box horizontal Left textcolor variable"
|
470
456
|
plot.ylabel "free space per page"
|
471
457
|
plot.xlabel "page number"
|
@@ -475,7 +461,7 @@ def space_index_pages_free_plot(space, image, start_page)
|
|
475
461
|
index_data.sort.each do |id, data|
|
476
462
|
plot.data << Gnuplot::DataSet.new([data[:x], data[:y]]) do |ds|
|
477
463
|
ds.with = "dots"
|
478
|
-
ds.title = id
|
464
|
+
ds.title = id.zero? ? "Unallocated" : "Index #{id}"
|
479
465
|
end
|
480
466
|
end
|
481
467
|
|
@@ -488,279 +474,99 @@ def space_extents(space)
|
|
488
474
|
print_xdes_list(space, space.each_xdes)
|
489
475
|
end
|
490
476
|
|
477
|
+
# rubocop:disable Metrics/BlockNesting
|
478
|
+
def space_extents_illustrate_page_status(space, entry, count_by_identifier, identifiers)
|
479
|
+
entry.each_page_status.each_with_object("".dup) do |(page_number, page_status), bitmap|
|
480
|
+
if page_number >= space.pages
|
481
|
+
bitmap << " "
|
482
|
+
next
|
483
|
+
end
|
484
|
+
|
485
|
+
used_fraction = 1.0
|
486
|
+
identifier = nil
|
487
|
+
if page_status[:free]
|
488
|
+
used_fraction = 0.0
|
489
|
+
else
|
490
|
+
page = space.page(page_number)
|
491
|
+
used_fraction = page.used_space.to_f / page.size if page.respond_to?(:used_space)
|
492
|
+
if page.respond_to?(:index_id)
|
493
|
+
identifier = page.index_id
|
494
|
+
unless identifiers[identifier]
|
495
|
+
identifiers[identifier] = page.ibuf_index? ? "Insert Buffer Index" : "Index #{page.index_id}"
|
496
|
+
if space.innodb_system
|
497
|
+
table, index = space.innodb_system.table_and_index_name_by_id(page.index_id)
|
498
|
+
identifiers[identifier] += " (%s.%s)" % [table, index] if table && index
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
bitmap << filled_block(used_fraction, identifier)
|
505
|
+
|
506
|
+
if used_fraction.zero?
|
507
|
+
count_by_identifier[:free] += 1
|
508
|
+
else
|
509
|
+
count_by_identifier[identifier] += 1
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
# rubocop:enable Metrics/BlockNesting
|
514
|
+
|
491
515
|
# Illustrate the space by printing each extent and for each page, printing a
|
492
516
|
# filled block colored based on the index the page is part of. Print a legend
|
493
517
|
# for the colors used afterwards.
|
494
518
|
def space_extents_illustrate(space)
|
495
|
-
|
519
|
+
width = space.pages_per_extent
|
496
520
|
puts
|
497
|
-
puts "%12s
|
521
|
+
puts "%12s %-#{width}s " % ["", center(space.name, width)]
|
522
|
+
puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
|
498
523
|
|
499
524
|
identifiers = {}
|
500
525
|
count_by_identifier = Hash.new(0)
|
501
526
|
|
502
527
|
space.each_xdes do |entry|
|
503
|
-
puts "%12i │%-#{
|
528
|
+
puts "%12i │%-#{width}s│" % [
|
504
529
|
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
|
-
},
|
530
|
+
space_extents_illustrate_page_status(space, entry, count_by_identifier, identifiers),
|
542
531
|
]
|
543
532
|
end
|
544
533
|
total_pages = count_by_identifier.values.reduce(:+)
|
545
534
|
|
546
|
-
puts "%12s ╰%-#{
|
535
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
547
536
|
|
548
537
|
puts
|
549
538
|
puts "Legend (%s = 1 page):" % [filled_block(1.0, nil)]
|
550
539
|
puts " %-62s %8s %8s" % [
|
551
|
-
"Page Type",
|
540
|
+
"Page Type",
|
541
|
+
"Pages",
|
542
|
+
"Ratio",
|
552
543
|
]
|
553
544
|
puts " %s %-60s %8i %7.2f%%" % [
|
554
545
|
filled_block(1.0, nil),
|
555
546
|
"System",
|
556
547
|
count_by_identifier[nil],
|
557
|
-
100.0 * (count_by_identifier[nil].to_f / total_pages
|
548
|
+
100.0 * (count_by_identifier[nil].to_f / total_pages),
|
558
549
|
]
|
559
550
|
identifiers.sort.each do |identifier, description|
|
560
551
|
puts " %s %-60s %8i %7.2f%%" % [
|
561
552
|
filled_block(1.0, identifier),
|
562
553
|
description,
|
563
554
|
count_by_identifier[identifier],
|
564
|
-
100.0 * (count_by_identifier[identifier].to_f / total_pages
|
555
|
+
100.0 * (count_by_identifier[identifier].to_f / total_pages),
|
565
556
|
]
|
566
557
|
end
|
567
558
|
puts " %s %-60s %8i %7.2f%%" % [
|
568
559
|
filled_block(0.0, nil),
|
569
560
|
"Free space",
|
570
561
|
count_by_identifier[:free],
|
571
|
-
100.0 * (count_by_identifier[:free].to_f / total_pages
|
562
|
+
100.0 * (count_by_identifier[:free].to_f / total_pages),
|
572
563
|
]
|
573
564
|
puts
|
574
565
|
end
|
575
566
|
|
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
|
-
line_width = space.pages_per_extent
|
618
|
-
block_size = @options.illustration_block_size
|
619
|
-
|
620
|
-
puts "<?xml version=\"1.0\"?>"
|
621
|
-
puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
|
622
|
-
|
623
|
-
identifiers = {}
|
624
|
-
count_by_identifier = Hash.new(0)
|
625
|
-
|
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
567
|
def space_lsn_age_illustrate(space)
|
762
568
|
colors = ANSI_COLORS_HEATMAP
|
763
|
-
|
569
|
+
width = @options.illustration_line_width
|
764
570
|
|
765
571
|
# Calculate the minimum and maximum LSN in the space. This is pretty
|
766
572
|
# inefficient as we end up scanning all pages twice.
|
@@ -768,70 +574,57 @@ def space_lsn_age_illustrate(space)
|
|
768
574
|
|
769
575
|
lsn_min = lsn_max = space.page(0).lsn
|
770
576
|
space.each_page do |page_number, page|
|
771
|
-
if page.lsn
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
577
|
+
next if page.lsn.zero?
|
578
|
+
|
579
|
+
page_lsn[page_number] = page.lsn
|
580
|
+
lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
|
581
|
+
lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
|
776
582
|
end
|
777
583
|
lsn_delta = lsn_max - lsn_min
|
778
584
|
|
779
585
|
puts
|
780
|
-
puts "%12s
|
586
|
+
puts "%12s %-#{width}s " % ["", center(space.name, width)]
|
587
|
+
puts "%12s ╭%-#{width}s╮" % ["Start Page", "─" * width]
|
781
588
|
|
782
589
|
start_page = 0
|
783
|
-
page_lsn.each_slice(
|
784
|
-
puts "%12i │%-#{
|
590
|
+
page_lsn.each_slice(width) do |slice|
|
591
|
+
puts "%12i │%-#{width}s│" % [
|
785
592
|
start_page,
|
786
|
-
slice.inject("")
|
593
|
+
slice.inject("") do |line, lsn|
|
787
594
|
if lsn
|
788
|
-
age_ratio = (lsn - lsn_min).to_f / lsn_delta
|
595
|
+
age_ratio = (lsn - lsn_min).to_f / lsn_delta
|
789
596
|
color = colors[(age_ratio * colors.size.to_f).floor]
|
790
597
|
line += ansi_color(color, filled_block(1.0, nil))
|
791
598
|
else
|
792
599
|
line += " "
|
793
600
|
end
|
794
601
|
line
|
795
|
-
|
602
|
+
end,
|
796
603
|
]
|
797
|
-
start_page +=
|
604
|
+
start_page += width
|
798
605
|
end
|
799
606
|
|
800
|
-
puts "%12s ╰%-#{
|
607
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
801
608
|
|
802
|
-
|
609
|
+
_, lsn_freq = page_lsn.reject(&:nil?).histogram(colors.size, min: lsn_min, max: lsn_max)
|
610
|
+
lsn_freq_delta = lsn_freq.max - lsn_freq.min
|
803
611
|
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
require 'histogram/array'
|
809
|
-
|
810
|
-
lsn_bins, lsn_freq = page_lsn.select { |lsn| !lsn.nil? }.
|
811
|
-
histogram(colors.size, :min => lsn_min, :max => lsn_max)
|
812
|
-
|
813
|
-
lsn_freq_delta = lsn_freq.max - lsn_freq.min
|
814
|
-
|
815
|
-
lsn_legend = ""
|
816
|
-
lsn_freq.each do |freq|
|
817
|
-
freq_norm = freq / lsn_freq_delta
|
818
|
-
if freq_norm > 0.0
|
819
|
-
lsn_legend << filled_block(freq_norm)
|
820
|
-
else
|
821
|
-
# Avoid the "empty" block used for 0.0.
|
822
|
-
lsn_legend << " "
|
823
|
-
end
|
824
|
-
end
|
825
|
-
rescue LoadError
|
826
|
-
# That's okay! Leave the legend boring.
|
612
|
+
lsn_age_histogram = "".dup
|
613
|
+
lsn_freq.each do |freq|
|
614
|
+
freq_norm = freq / lsn_freq_delta
|
615
|
+
lsn_age_histogram << (freq_norm > 0.0 ? filled_block(freq_norm) : " ")
|
827
616
|
end
|
828
617
|
|
829
618
|
puts
|
830
|
-
puts "
|
619
|
+
puts "LSN Age Histogram (%s = ~%d pages):" % [
|
620
|
+
filled_block(1.0, nil),
|
621
|
+
(space.pages.to_f / colors.size).round,
|
622
|
+
]
|
831
623
|
puts " %12s %s %-12s" % [
|
832
624
|
"Min LSN",
|
833
|
-
|
834
|
-
"Max LSN"
|
625
|
+
lsn_age_histogram,
|
626
|
+
"Max LSN",
|
627
|
+
]
|
835
628
|
puts " %12i %s %-12i" % [
|
836
629
|
lsn_min,
|
837
630
|
colors.map { |c| ansi_color(c, filled_block(1.0, nil)) }.join,
|
@@ -839,126 +632,6 @@ def space_lsn_age_illustrate(space)
|
|
839
632
|
]
|
840
633
|
end
|
841
634
|
|
842
|
-
def space_lsn_age_illustrate_svg(space)
|
843
|
-
colors = RGBHEX_COLORS_HEATMAP
|
844
|
-
line_width = @options.illustration_line_width
|
845
|
-
block_size = @options.illustration_block_size
|
846
|
-
|
847
|
-
puts "<?xml version=\"1.0\"?>"
|
848
|
-
puts "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">"
|
849
|
-
|
850
|
-
# Calculate the minimum and maximum LSN in the space. This is pretty
|
851
|
-
# inefficient as we end up scanning all pages twice.
|
852
|
-
page_lsn = Array.new(space.pages)
|
853
|
-
|
854
|
-
lsn_min = lsn_max = space.page(0).lsn
|
855
|
-
space.each_page do |page_number, page|
|
856
|
-
if page.lsn != 0
|
857
|
-
page_lsn[page_number] = page.lsn
|
858
|
-
lsn_min = page.lsn < lsn_min ? page.lsn : lsn_min
|
859
|
-
lsn_max = page.lsn > lsn_max ? page.lsn : lsn_max
|
860
|
-
end
|
861
|
-
end
|
862
|
-
lsn_delta = lsn_max - lsn_min
|
863
|
-
|
864
|
-
graphic_x = 48
|
865
|
-
graphic_y = 16
|
866
|
-
|
867
|
-
block_x = 0
|
868
|
-
block_y = 0
|
869
|
-
|
870
|
-
puts svg("text", {
|
871
|
-
"y" => graphic_y - 3,
|
872
|
-
"x" => graphic_x - 7,
|
873
|
-
"font-family" => "monospace",
|
874
|
-
"font-size" => block_size,
|
875
|
-
"font-weight" => "bold",
|
876
|
-
"text-anchor" => "end",
|
877
|
-
}, "Page")
|
878
|
-
|
879
|
-
start_page = 0
|
880
|
-
page_lsn.each_slice(line_width) do |slice|
|
881
|
-
block_x = 0
|
882
|
-
slice.each do |lsn|
|
883
|
-
rgbhex = ""
|
884
|
-
if lsn
|
885
|
-
age_ratio = (lsn - lsn_min).to_f / lsn_delta.to_f
|
886
|
-
color = colors[(age_ratio * colors.size.to_f).floor]
|
887
|
-
end
|
888
|
-
puts svg("rect", {
|
889
|
-
"y" => graphic_y + block_y,
|
890
|
-
"x" => graphic_x + block_x,
|
891
|
-
"width" => block_size,
|
892
|
-
"height" => block_size,
|
893
|
-
"fill" => color ? "#" + color : "black",
|
894
|
-
})
|
895
|
-
block_x += block_size
|
896
|
-
end
|
897
|
-
puts svg("text", {
|
898
|
-
"y" => graphic_y + block_y + block_size,
|
899
|
-
"x" => graphic_x - 7,
|
900
|
-
"font-family" => "monospace",
|
901
|
-
"font-size" => block_size,
|
902
|
-
"text-anchor" => "end",
|
903
|
-
}, start_page)
|
904
|
-
block_y += block_size
|
905
|
-
start_page += line_width
|
906
|
-
end
|
907
|
-
|
908
|
-
puts svg("path", {
|
909
|
-
"stroke" => "black",
|
910
|
-
"stroke-width" => 1,
|
911
|
-
"fill" => "none",
|
912
|
-
"d" => svg_path_rounded_rect(
|
913
|
-
graphic_x,
|
914
|
-
graphic_y,
|
915
|
-
line_width * block_size,
|
916
|
-
block_y,
|
917
|
-
4
|
918
|
-
),
|
919
|
-
})
|
920
|
-
|
921
|
-
block_x = 0
|
922
|
-
block_y += 16
|
923
|
-
puts svg("text", {
|
924
|
-
"y" => graphic_y + block_y + block_size - 4,
|
925
|
-
"x" => graphic_x + block_x,
|
926
|
-
"font-family" => "monospace",
|
927
|
-
"font-size" => block_size,
|
928
|
-
"text-anchor" => "start",
|
929
|
-
}, lsn_min)
|
930
|
-
color_width = ((64.0 * block_size.to_f) / colors.size.to_f).round
|
931
|
-
colors.each do |color|
|
932
|
-
puts svg("rect", {
|
933
|
-
"y" => graphic_y + block_y + block_size,
|
934
|
-
"x" => graphic_x + block_x,
|
935
|
-
"width" => color_width,
|
936
|
-
"height" => block_size,
|
937
|
-
"fill" => "#" + color,
|
938
|
-
})
|
939
|
-
block_x += color_width
|
940
|
-
end
|
941
|
-
puts svg("text", {
|
942
|
-
"y" => graphic_y + block_y + block_size - 4,
|
943
|
-
"x" => graphic_x + block_x,
|
944
|
-
"font-family" => "monospace",
|
945
|
-
"font-size" => block_size,
|
946
|
-
"text-anchor" => "end",
|
947
|
-
}, lsn_max)
|
948
|
-
|
949
|
-
puts svg("text", {
|
950
|
-
"y" => graphic_y + block_y + block_size - 4,
|
951
|
-
"x" => graphic_x + (block_x / 2),
|
952
|
-
"font-family" => "monospace",
|
953
|
-
"font-weight" => "bold",
|
954
|
-
"font-size" => block_size,
|
955
|
-
"text-anchor" => "middle",
|
956
|
-
}, "LSN Age")
|
957
|
-
|
958
|
-
|
959
|
-
puts "</svg>\n"
|
960
|
-
end
|
961
|
-
|
962
635
|
def print_inode_summary(inode)
|
963
636
|
puts "INODE fseg_id=%d, pages=%d, frag=%d, full=%d, not_full=%d, free=%d" % [
|
964
637
|
inode.fseg_id,
|
@@ -971,6 +644,7 @@ def print_inode_summary(inode)
|
|
971
644
|
end
|
972
645
|
|
973
646
|
def print_inode_detail(inode)
|
647
|
+
# rubocop:disable Layout/LineLength
|
974
648
|
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)" % [
|
975
649
|
inode.fseg_id,
|
976
650
|
inode.total_pages,
|
@@ -985,6 +659,13 @@ def print_inode_detail(inode)
|
|
985
659
|
inode.free.length,
|
986
660
|
inode.free.each.to_a.map { |x| "#{x.start_page}-#{x.end_page}" }.join(", "),
|
987
661
|
]
|
662
|
+
# rubocop:enable Layout/LineLength
|
663
|
+
end
|
664
|
+
|
665
|
+
def space_inodes_fseg_id(space)
|
666
|
+
space.each_inode do |inode|
|
667
|
+
puts inode.fseg_id
|
668
|
+
end
|
988
669
|
end
|
989
670
|
|
990
671
|
def space_inodes_summary(space)
|
@@ -1015,42 +696,6 @@ def page_account(innodb_system, space, page_number)
|
|
1015
696
|
page_type[:usage],
|
1016
697
|
]
|
1017
698
|
|
1018
|
-
if page.corrupt?
|
1019
|
-
puts " Page appears to be corrupt."
|
1020
|
-
puts " Stored checksum is %d, type %s." % [
|
1021
|
-
page.checksum,
|
1022
|
-
page.checksum_type ? page.checksum_type : "unknown",
|
1023
|
-
]
|
1024
|
-
puts " Calculated checksums:"
|
1025
|
-
puts " crc32 %d" % [page.checksum_crc32]
|
1026
|
-
puts " innodb %d" % [page.checksum_innodb]
|
1027
|
-
end
|
1028
|
-
|
1029
|
-
if page.torn?
|
1030
|
-
puts " Page appears to be torn."
|
1031
|
-
puts " Full LSN from header is %d." % [page.lsn]
|
1032
|
-
puts " Low 32 bits of LSN from header is %d, trailer is %d." % [
|
1033
|
-
page.lsn_low32_header,
|
1034
|
-
page.lsn_low32_trailer,
|
1035
|
-
]
|
1036
|
-
end
|
1037
|
-
|
1038
|
-
if page.misplaced?
|
1039
|
-
puts " Page appears to be misplaced."
|
1040
|
-
if page.misplaced_offset?
|
1041
|
-
puts " Requested page %d but offset stored in page is %d." % [
|
1042
|
-
page_number,
|
1043
|
-
page.offset,
|
1044
|
-
]
|
1045
|
-
end
|
1046
|
-
if page.misplaced_space?
|
1047
|
-
puts " Space's ID %d does not match page's stored space ID %d." % [
|
1048
|
-
page.space.space_id,
|
1049
|
-
page.space_id,
|
1050
|
-
]
|
1051
|
-
end
|
1052
|
-
end
|
1053
|
-
|
1054
699
|
xdes = space.xdes_for_page(page_number)
|
1055
700
|
puts " Extent descriptor for pages %d-%d is at page %d, offset %d." % [
|
1056
701
|
xdes.start_page,
|
@@ -1067,25 +712,23 @@ def page_account(innodb_system, space, page_number)
|
|
1067
712
|
|
1068
713
|
xdes_status = xdes.page_status(page_number)
|
1069
714
|
puts " Page is marked as %s in extent descriptor." % [
|
1070
|
-
xdes_status[:free] ?
|
715
|
+
xdes_status[:free] ? "free" : "used",
|
1071
716
|
]
|
1072
717
|
|
1073
718
|
space.each_xdes_list do |name, list|
|
1074
|
-
if list.include?
|
1075
|
-
puts " Extent is in #{name} list of space."
|
1076
|
-
end
|
719
|
+
puts " Extent is in #{name} list of space." if list.include?(xdes)
|
1077
720
|
end
|
1078
721
|
|
1079
722
|
page_inode = nil
|
1080
723
|
space.each_inode do |inode|
|
1081
724
|
inode.each_list do |name, list|
|
1082
|
-
if list.include?
|
725
|
+
if list.include?(xdes)
|
1083
726
|
page_inode = inode
|
1084
727
|
puts " Extent is in #{name} list of fseg #{inode.fseg_id}."
|
1085
728
|
end
|
1086
729
|
end
|
1087
730
|
|
1088
|
-
if inode.frag_array.include?
|
731
|
+
if inode.frag_array.include?(page_number) # rubocop:disable Style/Next
|
1089
732
|
page_inode = inode
|
1090
733
|
puts " Page is in fragment array of fseg %d." % [
|
1091
734
|
inode.fseg_id,
|
@@ -1095,65 +738,213 @@ def page_account(innodb_system, space, page_number)
|
|
1095
738
|
|
1096
739
|
space.each_index do |index|
|
1097
740
|
index.each_fseg do |fseg_name, fseg|
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
end
|
1106
|
-
end
|
741
|
+
next unless page_inode == fseg
|
742
|
+
|
743
|
+
puts " Fseg is in #{fseg_name} fseg of index #{index.id}."
|
744
|
+
puts " Index root is page #{index.root.offset}."
|
745
|
+
if innodb_system
|
746
|
+
table_name, index_name = innodb_system.table_and_index_name_by_id(index.id)
|
747
|
+
puts " Index is #{table_name}.#{index_name}." if table_name && index_name
|
1107
748
|
end
|
1108
749
|
end
|
1109
750
|
end
|
1110
751
|
|
1111
|
-
if space.system_space?
|
1112
|
-
if page_inode == space.trx_sys.fseg
|
1113
|
-
|
1114
|
-
end
|
1115
|
-
|
1116
|
-
if page_inode == space.trx_sys.doublewrite[:fseg]
|
1117
|
-
puts " Fseg is doublewrite buffer."
|
1118
|
-
end
|
752
|
+
if space.system_space? # rubocop:disable Style/GuardClause
|
753
|
+
puts " Fseg is trx_sys." if page_inode == space.trx_sys.fseg
|
754
|
+
puts " Fseg is doublewrite buffer." if page_inode == space.trx_sys.doublewrite[:fseg]
|
1119
755
|
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
if page_inode == fseg
|
1124
|
-
puts " Index is #{table_name}.#{index_name} of data dictionary."
|
1125
|
-
end
|
1126
|
-
end
|
756
|
+
innodb_system.data_dictionary&.each_data_dictionary_index do |table_name, index_name, index|
|
757
|
+
index.each_fseg do |_fseg_name, fseg|
|
758
|
+
puts " Index is #{table_name}.#{index_name} of data dictionary." if page_inode == fseg
|
1127
759
|
end
|
1128
760
|
end
|
1129
761
|
|
1130
762
|
space.trx_sys.rsegs.each_with_index do |rseg_slot, index|
|
1131
|
-
if page.fil_header
|
1132
|
-
|
1133
|
-
puts " Page is a rollback segment in slot #{index}."
|
763
|
+
if page.fil_header.space_id == rseg_slot.space_id && page.fil_header.offset == rseg_slot.page_number
|
764
|
+
puts " Page is a rollback segment in slot #{index}."
|
1134
765
|
end
|
1135
766
|
end
|
1136
767
|
end
|
1137
768
|
end
|
1138
769
|
|
1139
|
-
def
|
1140
|
-
|
1141
|
-
|
770
|
+
def page_validate_index(page)
|
771
|
+
page_is_valid = true
|
772
|
+
|
773
|
+
print "Parsing all records in page... "
|
774
|
+
records = page.each_record.to_a
|
775
|
+
puts "done."
|
776
|
+
|
777
|
+
directory_offsets = page.each_directory_offset.to_a
|
778
|
+
record_offsets = records.map(&:offset)
|
779
|
+
|
780
|
+
invalid_directory_entries = directory_offsets.reject { |n| record_offsets.include?(n) }
|
781
|
+
|
782
|
+
unless invalid_directory_entries.empty?
|
783
|
+
page_is_valid = false
|
784
|
+
puts "Invalid page directory entries (offsets not to valid records):"
|
785
|
+
invalid_directory_entries.each do |offset|
|
786
|
+
puts " slot %d, offset %d" % [
|
787
|
+
page.offset_is_directory_slot?(offset),
|
788
|
+
offset,
|
789
|
+
]
|
790
|
+
end
|
791
|
+
end
|
792
|
+
|
793
|
+
# Read all records corresponding to valid directory entries.
|
794
|
+
directory_records = directory_offsets.reject { |o| invalid_directory_entries.include?(o) }.map { |o| page.record(o) }
|
795
|
+
|
796
|
+
misordered_directory_entries = []
|
797
|
+
prev = nil
|
798
|
+
directory_records.each do |rec|
|
799
|
+
unless prev
|
800
|
+
prev = rec
|
801
|
+
next
|
802
|
+
end
|
803
|
+
if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
|
804
|
+
page_is_valid = false
|
805
|
+
misordered_directory_entries << {
|
806
|
+
slot: page.offset_is_directory_slot?(rec.offset),
|
807
|
+
offset: rec.offset,
|
808
|
+
key: rec.key_string,
|
809
|
+
prev_key: prev.key_string,
|
810
|
+
}
|
811
|
+
end
|
812
|
+
prev = rec
|
813
|
+
end
|
814
|
+
unless misordered_directory_entries.empty?
|
815
|
+
puts "Misordered page directory entries (key < prev key):"
|
816
|
+
misordered_directory_entries.each do |entry|
|
817
|
+
puts " slot %d, offset %d, key %s, prev key %s" % [
|
818
|
+
entry[:slot],
|
819
|
+
entry[:offset],
|
820
|
+
entry[:key],
|
821
|
+
entry[:prev_key],
|
822
|
+
]
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
misordered_records = []
|
827
|
+
prev = nil
|
828
|
+
page.each_record do |rec|
|
829
|
+
unless prev
|
830
|
+
prev = rec
|
831
|
+
next
|
832
|
+
end
|
833
|
+
if rec.compare_key(prev.key.map { |v| v[:value] }) == 1
|
834
|
+
page_is_valid = false
|
835
|
+
misordered_records << {
|
836
|
+
offset: rec.offset,
|
837
|
+
key: rec.key_string,
|
838
|
+
prev_key: prev.key_string,
|
839
|
+
}
|
840
|
+
end
|
841
|
+
prev = rec
|
842
|
+
end
|
843
|
+
unless misordered_records.empty?
|
844
|
+
puts "Misordered records in record list (key < prev key):"
|
845
|
+
misordered_records.each do |entry|
|
846
|
+
puts " offset %d, key %s, prev key %s" % [
|
847
|
+
entry[:offset],
|
848
|
+
entry[:key],
|
849
|
+
entry[:prev_key],
|
850
|
+
]
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
page_is_valid
|
855
|
+
end
|
856
|
+
|
857
|
+
def page_validate(_innodb_system, space, page_number)
|
858
|
+
page_is_valid = true
|
859
|
+
puts "Validating page %d..." % [page_number]
|
860
|
+
|
861
|
+
print "Parsing page... "
|
862
|
+
page = space.page(page_number)
|
863
|
+
puts "done."
|
864
|
+
|
865
|
+
if page.corrupt?
|
866
|
+
page_is_valid = false
|
867
|
+
puts "Page appears to be corrupt:"
|
868
|
+
puts " Stored checksums:"
|
869
|
+
puts " header %10d (0x%08x), type %s" % [
|
870
|
+
page.checksum,
|
871
|
+
page.checksum,
|
872
|
+
page.checksum_type || "unknown",
|
873
|
+
]
|
874
|
+
puts " trailer %10d (0x%08x)" % [
|
875
|
+
page.fil_trailer.checksum,
|
876
|
+
page.fil_trailer.checksum,
|
877
|
+
]
|
878
|
+
puts " Calculated checksums:"
|
879
|
+
puts " crc32 %10d (0x%08x)" % [
|
880
|
+
page.checksum_crc32,
|
881
|
+
page.checksum_crc32,
|
882
|
+
]
|
883
|
+
puts " innodb %10d (0x%08x)" % [
|
884
|
+
page.checksum_innodb,
|
885
|
+
page.checksum_innodb,
|
886
|
+
]
|
887
|
+
end
|
888
|
+
|
889
|
+
if page.torn?
|
890
|
+
page_is_valid = false
|
891
|
+
puts "Page appears to be torn:"
|
892
|
+
puts " Full LSN:"
|
893
|
+
puts " header %d (0x%016x)" % [
|
894
|
+
page.lsn,
|
895
|
+
page.lsn,
|
896
|
+
]
|
897
|
+
puts " Low 32 bits of LSN:"
|
898
|
+
puts " header %10d (0x%08x)" % [
|
899
|
+
page.fil_header.lsn_low32,
|
900
|
+
page.fil_header.lsn_low32,
|
901
|
+
]
|
902
|
+
puts " trailer %10d (0x%08x)" % [
|
903
|
+
page.fil_trailer.lsn_low32,
|
904
|
+
page.fil_trailer.lsn_low32,
|
905
|
+
]
|
906
|
+
end
|
907
|
+
|
908
|
+
if page.misplaced?
|
909
|
+
page_is_valid = false
|
910
|
+
puts "Page appears to be misplaced:"
|
911
|
+
if page.misplaced_offset?
|
912
|
+
puts " Requested page %d but offset stored in page is %d." % [
|
913
|
+
page_number,
|
914
|
+
page.offset,
|
915
|
+
]
|
916
|
+
end
|
917
|
+
if page.misplaced_space?
|
918
|
+
puts " Space ID %d does not match page stored space ID %d." % [
|
919
|
+
page.space.space_id,
|
920
|
+
page.space_id,
|
921
|
+
]
|
922
|
+
end
|
1142
923
|
end
|
1143
924
|
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
"
|
1149
|
-
|
925
|
+
page_is_valid = false if page.type == :INDEX && !page_validate_index(page)
|
926
|
+
|
927
|
+
puts "Page %d appears to be %s!" % [
|
928
|
+
page_number,
|
929
|
+
page_is_valid ? "valid" : "corrupted",
|
930
|
+
]
|
931
|
+
end
|
932
|
+
|
933
|
+
def page_directory_summary(_space, page)
|
934
|
+
usage(1, "Page must be an index page") if page.type != :INDEX
|
935
|
+
|
936
|
+
puts "%-8s%-8s%-14s%-8s%s" % %w[
|
937
|
+
slot
|
938
|
+
offset
|
939
|
+
type
|
940
|
+
owned
|
941
|
+
key
|
1150
942
|
]
|
1151
943
|
|
1152
944
|
page.directory.each_with_index do |offset, slot|
|
1153
945
|
record = page.record(offset)
|
1154
|
-
key =
|
1155
|
-
|
1156
|
-
end
|
946
|
+
key = %i[conventional node_pointer].include?(record.header[:type]) ? "(%s)" % record.key_string : ""
|
947
|
+
|
1157
948
|
puts "%-8i%-8i%-14s%-8i%s" % [
|
1158
949
|
slot,
|
1159
950
|
offset,
|
@@ -1164,57 +955,63 @@ def page_directory_summary(space, page)
|
|
1164
955
|
end
|
1165
956
|
end
|
1166
957
|
|
1167
|
-
def page_records(
|
958
|
+
def page_records(_space, page)
|
1168
959
|
page.each_record do |record|
|
1169
960
|
puts "Record %i: %s" % [
|
1170
961
|
record.offset,
|
1171
962
|
record.string,
|
1172
963
|
]
|
1173
|
-
puts if record.header[:type] == :conventional
|
1174
964
|
end
|
1175
965
|
end
|
1176
966
|
|
1177
967
|
def page_illustrate(page)
|
1178
968
|
width = 64
|
1179
|
-
|
969
|
+
unknown_page_content = page.type == :INDEX && page.record_describer.nil?
|
970
|
+
blocks = Array.new(page.size, unknown_page_content ? "▞" : " ")
|
1180
971
|
identifiers = {}
|
1181
972
|
identifier_sort = 0
|
1182
973
|
count_by_identifier = Hash.new(0)
|
1183
974
|
|
1184
|
-
page.each_region.
|
1185
|
-
region
|
975
|
+
page.each_region.sort_by(&:offset).each do |region|
|
976
|
+
region.length.times do |n|
|
1186
977
|
identifier = nil
|
1187
978
|
fraction = 0.0
|
1188
|
-
if region
|
1189
|
-
|
1190
|
-
|
1191
|
-
else
|
1192
|
-
fraction = 1.0
|
1193
|
-
end
|
1194
|
-
identifier = region[:name].hash.abs
|
979
|
+
if region.name != :garbage
|
980
|
+
fraction = n == region.length - 1 ? 0.5 : 1.0
|
981
|
+
identifier = region.name.hash.abs
|
1195
982
|
unless identifiers[identifier]
|
1196
983
|
# Prefix an integer <0123> on each name so that the legend can be
|
1197
984
|
# sorted by the appearance of each region in the page.
|
1198
985
|
identifiers[identifier] = "<%04i>%s" % [
|
1199
986
|
identifier_sort,
|
1200
|
-
region
|
987
|
+
region.info,
|
1201
988
|
]
|
1202
989
|
identifier_sort += 1
|
1203
990
|
end
|
1204
991
|
end
|
1205
|
-
blocks[region
|
992
|
+
blocks[region.offset + n] = filled_block(fraction, identifier, BLOCK_CHARS_H)
|
1206
993
|
count_by_identifier[identifier] += 1
|
1207
994
|
end
|
1208
995
|
end
|
1209
996
|
|
1210
997
|
puts
|
1211
|
-
puts "%12s
|
998
|
+
puts "%12s %-#{width}s " % ["", center("Page #{page.offset} (#{page.type})", width)]
|
999
|
+
puts "%12s ╭%-#{width}s╮" % ["Offset", "─" * width]
|
1212
1000
|
offset = 0
|
1001
|
+
skipped_lines = 0
|
1213
1002
|
blocks.each_slice(width) do |slice|
|
1214
|
-
|
1003
|
+
if slice.any? { |s| s != " " }
|
1004
|
+
if skipped_lines.positive?
|
1005
|
+
puts "%12s │%-#{width}s│" % ["...", ""]
|
1006
|
+
skipped_lines = 0
|
1007
|
+
end
|
1008
|
+
puts "%12i │%-s│" % [offset, slice.join]
|
1009
|
+
else
|
1010
|
+
skipped_lines += 1
|
1011
|
+
end
|
1215
1012
|
offset += width
|
1216
1013
|
end
|
1217
|
-
puts "%12s ╰%-#{width}s╯" % [
|
1014
|
+
puts "%12s ╰%-#{width}s╯" % ["", "─" * width]
|
1218
1015
|
|
1219
1016
|
puts
|
1220
1017
|
puts "Legend (%s = 1 byte):" % [filled_block(1.0, nil)]
|
@@ -1223,47 +1020,52 @@ def page_illustrate(page)
|
|
1223
1020
|
"Bytes",
|
1224
1021
|
"Ratio",
|
1225
1022
|
]
|
1226
|
-
identifiers.sort { |a,b| a[1] <=> b[1] }.each do |identifier, description|
|
1023
|
+
identifiers.sort { |a, b| a[1] <=> b[1] }.each do |identifier, description|
|
1227
1024
|
puts " %s %-30s %8i %7.2f%%" % [
|
1228
1025
|
filled_block(1.0, identifier),
|
1229
1026
|
description.gsub(/^<\d+>/, ""),
|
1230
1027
|
count_by_identifier[identifier],
|
1231
|
-
100.0 * (count_by_identifier[identifier].to_f / page.size
|
1028
|
+
100.0 * (count_by_identifier[identifier].to_f / page.size),
|
1232
1029
|
]
|
1233
1030
|
end
|
1234
1031
|
puts " %s %-30s %8i %7.2f%%" % [
|
1235
1032
|
filled_block(0.0, nil),
|
1236
1033
|
"Garbage",
|
1237
1034
|
count_by_identifier[nil],
|
1238
|
-
100.0 * (count_by_identifier[nil].to_f / page.size
|
1035
|
+
100.0 * (count_by_identifier[nil].to_f / page.size),
|
1239
1036
|
]
|
1240
|
-
free_space = page.size - count_by_identifier.inject(0) { |sum,(
|
1037
|
+
free_space = page.size - count_by_identifier.inject(0) { |sum, (_k, v)| sum + v }
|
1241
1038
|
puts " %s %-30s %8i %7.2f%%" % [
|
1242
|
-
" ",
|
1243
|
-
"Free",
|
1039
|
+
unknown_page_content ? "▞" : " ",
|
1040
|
+
unknown_page_content ? "Unknown (no data dictionary)" : "Free",
|
1244
1041
|
free_space,
|
1245
|
-
100.0 * (free_space.to_f / page.size
|
1042
|
+
100.0 * (free_space.to_f / page.size),
|
1246
1043
|
]
|
1247
1044
|
|
1045
|
+
if unknown_page_content
|
1046
|
+
puts
|
1047
|
+
puts "Note:"
|
1048
|
+
puts " Records could not be parsed because no data dictionary or record describer"
|
1049
|
+
puts " was available. Use -s instead of -f, or provide a record describer class."
|
1050
|
+
end
|
1051
|
+
|
1248
1052
|
puts
|
1249
1053
|
end
|
1250
1054
|
|
1251
1055
|
def record_dump(page, record_offset)
|
1252
|
-
|
1253
|
-
|
1254
|
-
end
|
1056
|
+
record = page.record(record_offset)
|
1057
|
+
raise "Record at offset #{record_offset} not found" unless record
|
1255
1058
|
|
1256
1059
|
record.dump
|
1060
|
+
rescue IOError
|
1061
|
+
raise "Record could not be read at offset #{record_offset}; is it a valid record offset?"
|
1257
1062
|
end
|
1258
1063
|
|
1259
1064
|
def record_history(page, record_offset)
|
1260
|
-
unless page.leaf?
|
1261
|
-
raise "Record is not located on a leaf page; no history available"
|
1262
|
-
end
|
1065
|
+
raise "Record is not located on a leaf page; no history available" unless page.leaf?
|
1263
1066
|
|
1264
|
-
|
1265
|
-
|
1266
|
-
end
|
1067
|
+
record = page.record(record_offset)
|
1068
|
+
raise "Record at offset #{record_offset} not found" unless record
|
1267
1069
|
|
1268
1070
|
puts "%-14s%-20s%s" % [
|
1269
1071
|
"Transaction",
|
@@ -1281,29 +1083,23 @@ def record_history(page, record_offset)
|
|
1281
1083
|
end
|
1282
1084
|
|
1283
1085
|
def index_fseg_lists(index, fseg_name)
|
1284
|
-
unless index.fseg(fseg_name)
|
1285
|
-
raise "File segment '#{fseg_name}' doesn't exist"
|
1286
|
-
end
|
1086
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
|
1287
1087
|
|
1288
1088
|
print_lists(index.each_fseg_list(index.fseg(fseg_name)))
|
1289
1089
|
end
|
1290
1090
|
|
1291
1091
|
def index_fseg_list_iterate(index, fseg_name, list_name)
|
1292
|
-
|
1293
|
-
|
1294
|
-
end
|
1092
|
+
fseg = index.fseg(fseg_name)
|
1093
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless fseg
|
1295
1094
|
|
1296
|
-
|
1297
|
-
|
1298
|
-
end
|
1095
|
+
list = fseg.list(list_name)
|
1096
|
+
raise "List '#{list_name}' doesn't exist" unless list
|
1299
1097
|
|
1300
1098
|
print_xdes_list(index.space, list)
|
1301
1099
|
end
|
1302
1100
|
|
1303
1101
|
def index_fseg_frag_pages(index, fseg_name)
|
1304
|
-
unless index.fseg(fseg_name)
|
1305
|
-
raise "File segment '#{fseg_name}' doesn't exist"
|
1306
|
-
end
|
1102
|
+
raise "File segment '#{fseg_name}' doesn't exist" unless index.fseg(fseg_name)
|
1307
1103
|
|
1308
1104
|
print_index_page_summary(index.each_fseg_frag_page(index.fseg(fseg_name)))
|
1309
1105
|
end
|
@@ -1318,17 +1114,17 @@ def index_recurse(index)
|
|
1318
1114
|
page.records,
|
1319
1115
|
page.record_space,
|
1320
1116
|
]
|
1321
|
-
if page.level
|
1117
|
+
if page.level.zero?
|
1322
1118
|
page.each_record do |record|
|
1323
1119
|
puts "%sRECORD: (%s) → (%s)" % [
|
1324
|
-
" " * (depth+1),
|
1120
|
+
" " * (depth + 1),
|
1325
1121
|
record.key_string,
|
1326
1122
|
record.row_string,
|
1327
1123
|
]
|
1328
1124
|
end
|
1329
1125
|
end
|
1330
1126
|
end,
|
1331
|
-
lambda do |
|
1127
|
+
lambda do |_parent_page, child_page, child_min_key, depth|
|
1332
1128
|
puts "%sNODE POINTER RECORD ≥ (%s) → #%i" % [
|
1333
1129
|
" " * depth,
|
1334
1130
|
child_min_key.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }.join(", "),
|
@@ -1339,13 +1135,13 @@ def index_recurse(index)
|
|
1339
1135
|
end
|
1340
1136
|
|
1341
1137
|
def index_record_offsets(index)
|
1342
|
-
puts "%-20s%-20s" % [
|
1343
|
-
|
1344
|
-
|
1138
|
+
puts "%-20s%-20s" % %w[
|
1139
|
+
page_offset
|
1140
|
+
record_offset
|
1345
1141
|
]
|
1346
1142
|
index.recurse(
|
1347
|
-
lambda do |page,
|
1348
|
-
if page.level
|
1143
|
+
lambda do |page, _depth|
|
1144
|
+
if page.level.zero?
|
1349
1145
|
page.each_record do |record|
|
1350
1146
|
puts "%-20i%-20i" % [
|
1351
1147
|
page.offset,
|
@@ -1354,7 +1150,7 @@ def index_record_offsets(index)
|
|
1354
1150
|
end
|
1355
1151
|
end
|
1356
1152
|
end,
|
1357
|
-
|
1153
|
+
->(*_) {}
|
1358
1154
|
)
|
1359
1155
|
end
|
1360
1156
|
|
@@ -1374,13 +1170,13 @@ def index_digraph(index)
|
|
1374
1170
|
child_key.join(", "),
|
1375
1171
|
]
|
1376
1172
|
end
|
1377
|
-
puts " %spage_%i [ shape =
|
1173
|
+
puts " %spage_%i [ shape = 'record'; label = '%s'; ];" % [
|
1378
1174
|
" " * depth,
|
1379
1175
|
page.offset,
|
1380
1176
|
label,
|
1381
1177
|
]
|
1382
1178
|
end,
|
1383
|
-
lambda do |parent_page, child_page,
|
1179
|
+
lambda do |parent_page, child_page, _child_key, depth|
|
1384
1180
|
puts " %spage_%i:dir_%i → page_%i:page:nw;" % [
|
1385
1181
|
" " * depth,
|
1386
1182
|
parent_page.offset,
|
@@ -1393,14 +1189,14 @@ def index_digraph(index)
|
|
1393
1189
|
end
|
1394
1190
|
|
1395
1191
|
def index_level_summary(index, level)
|
1396
|
-
puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % [
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1192
|
+
puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % %w[
|
1193
|
+
page
|
1194
|
+
index
|
1195
|
+
level
|
1196
|
+
data
|
1197
|
+
free
|
1198
|
+
records
|
1199
|
+
min_key
|
1404
1200
|
]
|
1405
1201
|
|
1406
1202
|
index.each_page_at_level(level) do |page|
|
@@ -1417,15 +1213,14 @@ def index_level_summary(index, level)
|
|
1417
1213
|
end
|
1418
1214
|
|
1419
1215
|
def undo_history_summary(innodb_system)
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
"Table",
|
1216
|
+
history_list = innodb_system.history.each_history_list.reject { |h| h.list.empty? }
|
1217
|
+
|
1218
|
+
puts "%-8s%-8s%-14s%-20s%s" % %w[
|
1219
|
+
Page
|
1220
|
+
Offset
|
1221
|
+
Transaction
|
1222
|
+
Type
|
1223
|
+
Table
|
1429
1224
|
]
|
1430
1225
|
|
1431
1226
|
history_list.each do |history|
|
@@ -1455,20 +1250,22 @@ def usage(exit_code, message = nil)
|
|
1455
1250
|
exit exit_code
|
1456
1251
|
end
|
1457
1252
|
|
1253
|
+
# rubocop:disable Layout/HeredocIndentation
|
1458
1254
|
print <<'END_OF_USAGE'
|
1459
1255
|
|
1460
1256
|
Usage: innodb_space <options> <mode>
|
1461
1257
|
|
1462
1258
|
Invocation examples:
|
1463
1259
|
|
1464
|
-
innodb_space -s ibdata1 [-T
|
1465
|
-
Use ibdata1 as the system tablespace and load the
|
1466
|
-
|
1467
|
-
tablespace data dictionary. This will automatically generate a
|
1468
|
-
describer for any indexes.
|
1260
|
+
innodb_space -s ibdata1 [-T table-name [-I index-name [-R record-offset]]] [options] <mode>
|
1261
|
+
Use ibdata1 as the system tablespace and load the table-name table (and
|
1262
|
+
the index-name index for modes that require it) from data located in the
|
1263
|
+
system tablespace data dictionary. This will automatically generate a
|
1264
|
+
record describer for any indexes using the data dictionary.
|
1469
1265
|
|
1470
|
-
innodb_space -f
|
1471
|
-
Use the
|
1266
|
+
innodb_space -f file-name.ibd [-r ./describer.rb -d DescriberClass] [options] <mode>
|
1267
|
+
Use the file-name.ibd tablespace file (and the DescriberClass describer
|
1268
|
+
where required) to read the tablespace structures or indexes.
|
1472
1269
|
|
1473
1270
|
The following options are supported:
|
1474
1271
|
|
@@ -1482,16 +1279,27 @@ The following options are supported:
|
|
1482
1279
|
|
1483
1280
|
--system-space-file, -s <arg>
|
1484
1281
|
Load the system tablespace file or files <arg>: Either a single file e.g.
|
1485
|
-
|
1282
|
+
'ibdata1', a comma-delimited list of files e.g. 'ibdata1,ibdata1', or a
|
1486
1283
|
directory name. If a directory name is provided, it will be scanned for all
|
1487
|
-
files named
|
1284
|
+
files named 'ibdata?' which will then be sorted alphabetically and used to
|
1488
1285
|
load the system tablespace.
|
1489
1286
|
|
1490
|
-
--
|
1491
|
-
|
1287
|
+
If using the --system-space-file option, the following options may also
|
1288
|
+
be used:
|
1289
|
+
|
1290
|
+
--table-name, -T <name>
|
1291
|
+
Use the table name <name>.
|
1292
|
+
|
1293
|
+
--index-name, -I <name>
|
1294
|
+
Use the index name <name>.
|
1492
1295
|
|
1493
|
-
|
1494
|
-
|
1296
|
+
--system-space-tables, -x
|
1297
|
+
Allow opening tables from the system space to support system spaces with
|
1298
|
+
tables created without innodb-file-per-table enabled.
|
1299
|
+
|
1300
|
+
--data-directory, -D <directory>
|
1301
|
+
Open per-table tablespace files from <directory> rather than from the
|
1302
|
+
directory where the system-space-file is located.
|
1495
1303
|
|
1496
1304
|
--space-file, -f <file>
|
1497
1305
|
Load the tablespace file <file>.
|
@@ -1499,14 +1307,20 @@ The following options are supported:
|
|
1499
1307
|
--page, -p <page>
|
1500
1308
|
Operate on the page <page>.
|
1501
1309
|
|
1310
|
+
--record, -R <offset>
|
1311
|
+
Operate on the record located at <offset> within the index page.
|
1312
|
+
|
1502
1313
|
--level, -l <level>
|
1503
1314
|
Operate on the level <level>.
|
1504
1315
|
|
1505
1316
|
--list, -L <list>
|
1506
1317
|
Operate on the list <list>.
|
1507
1318
|
|
1319
|
+
--fseg-id, -F <fseg_id>
|
1320
|
+
Operate on the file segment (fseg) <fseg_id>.
|
1321
|
+
|
1508
1322
|
--require, -r <file>
|
1509
|
-
Use Ruby's
|
1323
|
+
Use Ruby's 'require' to load the file <file>. This is useful for loading
|
1510
1324
|
classes with record describers.
|
1511
1325
|
|
1512
1326
|
--describer, -d <describer>
|
@@ -1534,14 +1348,18 @@ The following modes are supported:
|
|
1534
1348
|
provided with the --page/-p argument.
|
1535
1349
|
|
1536
1350
|
space-index-pages-summary
|
1537
|
-
Summarize all
|
1538
|
-
page fill rates and record counts per page. In addition to
|
1539
|
-
|
1351
|
+
Summarize all 'INDEX' pages within a tablespace. This is useful to analyze
|
1352
|
+
page fill rates and record counts per page. In addition to 'INDEX' pages,
|
1353
|
+
'ALLOCATED' pages are also printed and assumed to be completely empty.
|
1540
1354
|
A starting page number can be provided with the --page/-p argument.
|
1541
1355
|
|
1356
|
+
space-index-fseg-pages-summary
|
1357
|
+
The same as space-index-pages-summary but only iterate one fseg, provided
|
1358
|
+
with the --fseg-id/-F argument.
|
1359
|
+
|
1542
1360
|
space-index-pages-free-plot
|
1543
1361
|
Use Ruby's gnuplot module to produce a scatterplot of page free space for
|
1544
|
-
all
|
1362
|
+
all 'INDEX' and 'ALLOCATED' pages in a tablespace. More aesthetically
|
1545
1363
|
pleasing plots can be produced with space-index-pages-summary output,
|
1546
1364
|
but this is a quick and easy way to produce a passable plot. A starting
|
1547
1365
|
page number can be provided with the --page/-p argument.
|
@@ -1573,19 +1391,13 @@ The following modes are supported:
|
|
1573
1391
|
color and Unicode box drawing characters to show page usage throughout
|
1574
1392
|
the space.
|
1575
1393
|
|
1576
|
-
space-extents-illustrate-svg
|
1577
|
-
Iterate through all extents, illustrating the extent usage in SVG format
|
1578
|
-
printed to stdout to show page usage throughout the space.
|
1579
|
-
|
1580
1394
|
space-lsn-age-illustrate
|
1581
1395
|
Iterate through all pages, producing a heat map colored by the page LSN
|
1582
1396
|
using ANSI color and Unicode box drawing characters, allowing the user to
|
1583
1397
|
get an overview of page modification recency.
|
1584
1398
|
|
1585
|
-
space-
|
1586
|
-
Iterate through all
|
1587
|
-
producing SVG format output, allowing the user to get an overview of page
|
1588
|
-
modification recency.
|
1399
|
+
space-inodes-fseg-id
|
1400
|
+
Iterate through all inodes, printing only the FSEG ID.
|
1589
1401
|
|
1590
1402
|
space-inodes-summary
|
1591
1403
|
Iterate through all inodes, printing a short summary of each FSEG.
|
@@ -1621,7 +1433,7 @@ The following modes are supported:
|
|
1621
1433
|
Iterate the file segment list (whose name is provided in the first --list/-L
|
1622
1434
|
argument) for internal or leaf pages for a given index (whose root page
|
1623
1435
|
is provided in the first --page/-p argument). The lists used for each
|
1624
|
-
index are
|
1436
|
+
index are 'full', 'not_full', and 'free'.
|
1625
1437
|
|
1626
1438
|
index-fseg-internal-frag-pages
|
1627
1439
|
index-fseg-leaf-frag-pages
|
@@ -1629,11 +1441,14 @@ The following modes are supported:
|
|
1629
1441
|
page must be provided with --page/-p.
|
1630
1442
|
|
1631
1443
|
page-dump
|
1632
|
-
Dump the contents of a page, using the Ruby pp (
|
1444
|
+
Dump the contents of a page, using the Ruby pp ('pretty-print') module.
|
1633
1445
|
|
1634
1446
|
page-account
|
1635
1447
|
Account for a page's usage in FSEGs.
|
1636
1448
|
|
1449
|
+
page-validate
|
1450
|
+
Validate the contents of a page.
|
1451
|
+
|
1637
1452
|
page-directory-summary
|
1638
1453
|
Summarize the record contents of the page directory in a page. If a record
|
1639
1454
|
describer is available, the key of each record will be printed.
|
@@ -1660,16 +1475,20 @@ The following modes are supported:
|
|
1660
1475
|
A record offset must be provided with -R/--record.
|
1661
1476
|
|
1662
1477
|
END_OF_USAGE
|
1478
|
+
# rubocop:enable Layout/HeredocIndentation
|
1663
1479
|
|
1664
1480
|
exit exit_code
|
1665
1481
|
end
|
1666
1482
|
|
1667
|
-
|
1668
|
-
Signal.trap(
|
1483
|
+
%w[INT PIPE].each do |name|
|
1484
|
+
Signal.trap(name) { exit } if Signal.list.include?(name)
|
1485
|
+
end
|
1669
1486
|
|
1670
1487
|
@options = OpenStruct.new
|
1671
1488
|
@options.trace = 0
|
1672
1489
|
@options.system_space_file = nil
|
1490
|
+
@options.system_space_tables = false
|
1491
|
+
@options.data_directory = nil
|
1673
1492
|
@options.space_file = nil
|
1674
1493
|
@options.table_name = nil
|
1675
1494
|
@options.index_name = nil
|
@@ -1677,14 +1496,18 @@ Signal.trap("PIPE") { exit }
|
|
1677
1496
|
@options.record = nil
|
1678
1497
|
@options.level = nil
|
1679
1498
|
@options.list = nil
|
1499
|
+
@options.fseg_id = nil
|
1680
1500
|
@options.describer = nil
|
1681
1501
|
@options.illustration_line_width = 64
|
1682
1502
|
@options.illustration_block_size = 8
|
1683
1503
|
|
1504
|
+
# rubocop:disable Layout/SpaceInsideArrayLiteralBrackets
|
1684
1505
|
getopt_options = [
|
1685
1506
|
[ "--help", "-?", GetoptLong::NO_ARGUMENT ],
|
1686
1507
|
[ "--trace", "-t", GetoptLong::NO_ARGUMENT ],
|
1687
1508
|
[ "--system-space-file", "-s", GetoptLong::REQUIRED_ARGUMENT ],
|
1509
|
+
[ "--system-space-tables", "-x", GetoptLong::NO_ARGUMENT ],
|
1510
|
+
[ "--data-directory", "-D", GetoptLong::REQUIRED_ARGUMENT ],
|
1688
1511
|
[ "--space-file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
|
1689
1512
|
[ "--table-name", "-T", GetoptLong::REQUIRED_ARGUMENT ],
|
1690
1513
|
[ "--index-name", "-I", GetoptLong::REQUIRED_ARGUMENT ],
|
@@ -1692,11 +1515,13 @@ getopt_options = [
|
|
1692
1515
|
[ "--record", "-R", GetoptLong::REQUIRED_ARGUMENT ],
|
1693
1516
|
[ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
|
1694
1517
|
[ "--list", "-L", GetoptLong::REQUIRED_ARGUMENT ],
|
1518
|
+
[ "--fseg-id", "-F", GetoptLong::REQUIRED_ARGUMENT ],
|
1695
1519
|
[ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
1696
1520
|
[ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
|
1697
1521
|
[ "--illustration-line-width", GetoptLong::REQUIRED_ARGUMENT ],
|
1698
1522
|
[ "--illustration-block-size", GetoptLong::REQUIRED_ARGUMENT ],
|
1699
1523
|
]
|
1524
|
+
# rubocop:enable Layout/SpaceInsideArrayLiteralBrackets
|
1700
1525
|
|
1701
1526
|
getopt = GetoptLong.new(*getopt_options)
|
1702
1527
|
|
@@ -1708,6 +1533,10 @@ getopt.each do |opt, arg|
|
|
1708
1533
|
@options.trace += 1
|
1709
1534
|
when "--system-space-file"
|
1710
1535
|
@options.system_space_file = arg.split(",")
|
1536
|
+
when "--system-space-tables"
|
1537
|
+
@options.system_space_tables = true
|
1538
|
+
when "--data-directory"
|
1539
|
+
@options.data_directory = arg
|
1711
1540
|
when "--space-file"
|
1712
1541
|
@options.space_file = arg.split(",")
|
1713
1542
|
when "--table-name"
|
@@ -1722,6 +1551,8 @@ getopt.each do |opt, arg|
|
|
1722
1551
|
@options.level = arg.to_i
|
1723
1552
|
when "--list"
|
1724
1553
|
@options.list = arg.to_sym
|
1554
|
+
when "--fseg-id"
|
1555
|
+
@options.fseg_id = arg.to_i
|
1725
1556
|
when "--require"
|
1726
1557
|
require File.expand_path(arg)
|
1727
1558
|
when "--describer"
|
@@ -1733,18 +1564,31 @@ getopt.each do |opt, arg|
|
|
1733
1564
|
end
|
1734
1565
|
end
|
1735
1566
|
|
1736
|
-
|
1737
|
-
|
1567
|
+
# rubocop:disable Style/IfUnlessModifier
|
1568
|
+
|
1569
|
+
unless @options.system_space_file || @options.space_file
|
1570
|
+
usage(1, "Either the --system-space-file (-s) or --space-file (-f) must be specified")
|
1738
1571
|
end
|
1739
1572
|
|
1740
|
-
if @options.system_space_file
|
1741
|
-
usage
|
1573
|
+
if @options.system_space_file && @options.space_file
|
1574
|
+
usage(1, "Only one of --system-space-file (-s) or --space-file (-f) may be specified")
|
1742
1575
|
end
|
1743
1576
|
|
1744
|
-
|
1745
|
-
|
1577
|
+
system_space_options =
|
1578
|
+
@options.table_name || @options.index_name || @options.system_space_tables || @options.data_directory
|
1579
|
+
|
1580
|
+
if !@options.system_space_file && system_space_options
|
1581
|
+
usage(
|
1582
|
+
1,
|
1583
|
+
%{
|
1584
|
+
The --table-name (-T), --index-name (-I), --system-space-tables (-x), and --data-directory (-D)
|
1585
|
+
options can only be used with the --system-space-file (-s) option
|
1586
|
+
}.squish
|
1587
|
+
)
|
1746
1588
|
end
|
1747
1589
|
|
1590
|
+
BufferCursor.trace! if @options.trace > 1
|
1591
|
+
|
1748
1592
|
# A few globals that we'll try to populate from the command-line arguments.
|
1749
1593
|
innodb_system = nil
|
1750
1594
|
space = nil
|
@@ -1752,12 +1596,18 @@ index = nil
|
|
1752
1596
|
page = nil
|
1753
1597
|
|
1754
1598
|
if @options.system_space_file
|
1755
|
-
innodb_system = Innodb::System.new(@options.system_space_file)
|
1599
|
+
innodb_system = Innodb::System.new(@options.system_space_file, data_directory: @options.data_directory)
|
1756
1600
|
end
|
1757
1601
|
|
1758
|
-
if innodb_system
|
1602
|
+
if innodb_system && @options.table_name
|
1759
1603
|
table_tablespace = innodb_system.space_by_table_name(@options.table_name)
|
1760
|
-
|
1604
|
+
if table_tablespace
|
1605
|
+
space = table_tablespace
|
1606
|
+
elsif @options.system_space_tables
|
1607
|
+
space = innodb_system.system_space
|
1608
|
+
else
|
1609
|
+
raise "Tablespace file not found and --system-space-tables (-x) is not enabled"
|
1610
|
+
end
|
1761
1611
|
elsif @options.space_file
|
1762
1612
|
space = Innodb::Space.new(@options.space_file)
|
1763
1613
|
else
|
@@ -1765,24 +1615,17 @@ else
|
|
1765
1615
|
end
|
1766
1616
|
|
1767
1617
|
if @options.describer
|
1768
|
-
describer = eval(@options.describer)
|
1769
|
-
|
1770
|
-
describer = Innodb::RecordDescriber.const_get(@options.describer)
|
1771
|
-
end
|
1618
|
+
describer = eval(@options.describer) # rubocop:disable Security/Eval
|
1619
|
+
describer ||= Innodb::RecordDescriber.const_get(@options.describer)
|
1772
1620
|
space.record_describer = describer.new
|
1773
1621
|
end
|
1774
1622
|
|
1775
|
-
if innodb_system
|
1623
|
+
if innodb_system && @options.table_name && @options.index_name
|
1776
1624
|
index = innodb_system.index_by_name(@options.table_name, @options.index_name)
|
1777
|
-
|
1778
|
-
page = space.page(@options.page)
|
1779
|
-
else
|
1780
|
-
page = index.root
|
1781
|
-
end
|
1625
|
+
page = @options.page ? space.page(@options.page) : index.root
|
1782
1626
|
elsif @options.page
|
1783
|
-
|
1784
|
-
|
1785
|
-
end
|
1627
|
+
page = space.page(@options.page)
|
1628
|
+
index = space.index(@options.page) if page&.type == :INDEX && page&.root?
|
1786
1629
|
end
|
1787
1630
|
|
1788
1631
|
# The non-option argument on the command line is the mode (usually the last,
|
@@ -1790,50 +1633,82 @@ end
|
|
1790
1633
|
mode = ARGV.shift
|
1791
1634
|
|
1792
1635
|
unless mode
|
1793
|
-
usage
|
1636
|
+
usage(1, "At least one mode must be provided")
|
1794
1637
|
end
|
1795
1638
|
|
1796
|
-
if /^(system-|data-dictionary-)/.match(mode)
|
1797
|
-
usage
|
1639
|
+
if /^(system-|data-dictionary-)/.match(mode) && !innodb_system
|
1640
|
+
usage(1, "System tablespace must be specified using --system-space-file (-s)")
|
1798
1641
|
end
|
1799
1642
|
|
1800
|
-
if /^space-/.match(mode)
|
1801
|
-
usage
|
1643
|
+
if /^space-/.match(mode) && !space
|
1644
|
+
usage(
|
1645
|
+
1,
|
1646
|
+
%{
|
1647
|
+
Tablespace must be specified using either --space-file (-f)
|
1648
|
+
or a combination of --system-space-file (-s) and --table (-T)
|
1649
|
+
}.squish
|
1650
|
+
)
|
1802
1651
|
end
|
1803
1652
|
|
1804
|
-
if /^index-/.match(mode)
|
1805
|
-
usage
|
1653
|
+
if /^index-/.match(mode) && !index
|
1654
|
+
usage(
|
1655
|
+
1,
|
1656
|
+
%{
|
1657
|
+
Index must be specified using a combination of either --space-file (-f) and --page (-p)
|
1658
|
+
or --system-space-file (-s), --table-name (-T), and --index-name (-I)
|
1659
|
+
}.squish
|
1660
|
+
)
|
1806
1661
|
end
|
1807
1662
|
|
1808
|
-
if /^page-/.match(mode)
|
1809
|
-
usage
|
1663
|
+
if /^page-/.match(mode) && !page
|
1664
|
+
usage(1, "Page number must be specified using --page (-p)")
|
1810
1665
|
end
|
1811
1666
|
|
1812
|
-
if /^record-/.match(mode)
|
1813
|
-
usage
|
1667
|
+
if /^record-/.match(mode) && !@options.record
|
1668
|
+
usage(1, "Record offset must be specified using --record (-R)")
|
1814
1669
|
end
|
1815
1670
|
|
1816
|
-
if
|
1817
|
-
usage
|
1671
|
+
if /^record-/.match(mode) && !page
|
1672
|
+
usage(
|
1673
|
+
1,
|
1674
|
+
%{
|
1675
|
+
An index page must be available when using --record (-R); specify either
|
1676
|
+
--page (-p) or --table-name (-T) and --index-name (-I) for the index root page.
|
1677
|
+
}
|
1678
|
+
)
|
1818
1679
|
end
|
1819
1680
|
|
1820
|
-
if
|
1821
|
-
usage
|
1681
|
+
if /^record-/.match(mode) && page.type != :INDEX
|
1682
|
+
usage(1, "Mode #{mode} may be used only with index pages")
|
1822
1683
|
end
|
1823
1684
|
|
1824
|
-
if
|
1825
|
-
"
|
1826
|
-
"index-record-offsets",
|
1827
|
-
"index-digraph",
|
1828
|
-
"index-level-summary",
|
1829
|
-
].include?(mode) and !index.record_describer
|
1830
|
-
usage 1, "Record describer must be specified using -d/--describer"
|
1685
|
+
if /-list-iterate$/.match(mode) && !@options.list
|
1686
|
+
usage(1, "List name must be specified using --list (-L)")
|
1831
1687
|
end
|
1832
1688
|
|
1833
|
-
if
|
1834
|
-
|
1689
|
+
if /-level-/.match(mode) && !@options.level
|
1690
|
+
usage(1, "Level must be specified using --level (-l)")
|
1835
1691
|
end
|
1836
1692
|
|
1693
|
+
if %w[
|
1694
|
+
index-recurse
|
1695
|
+
index-record-offsets
|
1696
|
+
index-digraph
|
1697
|
+
index-level-summary
|
1698
|
+
].include?(mode) && !index.record_describer
|
1699
|
+
usage(1, "Record describer must be specified using --describer (-d)")
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
if %w[
|
1703
|
+
space-index-fseg-pages-summary
|
1704
|
+
].include?(mode) && !@options.fseg_id
|
1705
|
+
usage(1, "File segment id must be specified using --fseg-id (-F)")
|
1706
|
+
end
|
1707
|
+
|
1708
|
+
# rubocop:enable Style/IfUnlessModifier
|
1709
|
+
|
1710
|
+
BufferCursor.trace! if @options.trace.positive?
|
1711
|
+
|
1837
1712
|
case mode
|
1838
1713
|
when "system-spaces"
|
1839
1714
|
system_spaces(innodb_system)
|
@@ -1849,9 +1724,10 @@ when "space-summary"
|
|
1849
1724
|
space_summary(space, @options.page || 0)
|
1850
1725
|
when "space-index-pages-summary"
|
1851
1726
|
space_index_pages_summary(space, @options.page || 0)
|
1727
|
+
when "space-index-fseg-pages-summary"
|
1728
|
+
space_index_fseg_pages_summary(space, @options.fseg_id)
|
1852
1729
|
when "space-index-pages-free-plot"
|
1853
|
-
|
1854
|
-
space_index_pages_free_plot(space, file_name, @options.page || 0)
|
1730
|
+
space_index_pages_free_plot(space, @options.page || 0)
|
1855
1731
|
when "space-page-type-regions"
|
1856
1732
|
space_page_type_regions(space, @options.page || 0)
|
1857
1733
|
when "space-page-type-summary"
|
@@ -1866,12 +1742,10 @@ when "space-extents"
|
|
1866
1742
|
space_extents(space)
|
1867
1743
|
when "space-extents-illustrate"
|
1868
1744
|
space_extents_illustrate(space)
|
1869
|
-
when "space-extents-illustrate-svg"
|
1870
|
-
space_extents_illustrate_svg(space)
|
1871
1745
|
when "space-lsn-age-illustrate"
|
1872
1746
|
space_lsn_age_illustrate(space)
|
1873
|
-
when "space-
|
1874
|
-
|
1747
|
+
when "space-inodes-fseg-id"
|
1748
|
+
space_inodes_fseg_id(space)
|
1875
1749
|
when "space-inodes-summary"
|
1876
1750
|
space_inodes_summary(space)
|
1877
1751
|
when "space-inodes-detail"
|
@@ -1900,6 +1774,8 @@ when "page-dump"
|
|
1900
1774
|
page.dump
|
1901
1775
|
when "page-account"
|
1902
1776
|
page_account(innodb_system, space, @options.page)
|
1777
|
+
when "page-validate"
|
1778
|
+
page_validate(innodb_system, space, @options.page)
|
1903
1779
|
when "page-directory-summary"
|
1904
1780
|
page_directory_summary(space, page)
|
1905
1781
|
when "page-records"
|