innodb_ruby 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/{innodb_dump_log → innodb_log} +0 -0
- data/bin/innodb_space +306 -0
- data/lib/innodb.rb +1 -0
- data/lib/innodb/index.rb +2 -2
- data/lib/innodb/log.rb +12 -6
- data/lib/innodb/log_block.rb +3 -3
- data/lib/innodb/page.rb +7 -2
- data/lib/innodb/page/fsp_hdr_xdes.rb +2 -2
- data/lib/innodb/page/index.rb +7 -7
- data/lib/innodb/page/inode.rb +1 -1
- data/lib/innodb/record_describer.rb +1 -0
- data/lib/innodb/space.rb +8 -3
- data/lib/innodb/version.rb +1 -1
- metadata +10 -9
- data/bin/innodb_dump_space +0 -189
File without changes
|
data/bin/innodb_space
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "getoptlong"
|
4
|
+
require "ostruct"
|
5
|
+
require "pp"
|
6
|
+
require "innodb"
|
7
|
+
|
8
|
+
def space_summary(space)
|
9
|
+
puts "%-12s%-20s%-12s%-12s%-20s" % [
|
10
|
+
"page",
|
11
|
+
"type",
|
12
|
+
"prev",
|
13
|
+
"next",
|
14
|
+
"lsn",
|
15
|
+
]
|
16
|
+
|
17
|
+
space.each_page do |page_number, page|
|
18
|
+
puts "%-12i%-20s%-12i%-12i%-20i" % [
|
19
|
+
page_number,
|
20
|
+
page.type,
|
21
|
+
page.prev,
|
22
|
+
page.next,
|
23
|
+
page.lsn,
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def space_index_pages_summary(space)
|
29
|
+
puts "%-12s%-8s%-8s%-8s%-8s%-8s" % [
|
30
|
+
"page",
|
31
|
+
"index",
|
32
|
+
"level",
|
33
|
+
"data",
|
34
|
+
"free",
|
35
|
+
"records",
|
36
|
+
]
|
37
|
+
|
38
|
+
space.each_page do |page_number, page|
|
39
|
+
case page.type
|
40
|
+
when :INDEX
|
41
|
+
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [
|
42
|
+
page_number,
|
43
|
+
page.ph[:index_id],
|
44
|
+
page.ph[:level],
|
45
|
+
page.record_space,
|
46
|
+
page.free_space,
|
47
|
+
page.ph[:n_recs],
|
48
|
+
]
|
49
|
+
when :ALLOCATED
|
50
|
+
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [ page_number, 0, 0, 0, page.size, 0 ]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def index_recurse(index)
|
56
|
+
index.recurse(
|
57
|
+
lambda do |page, depth|
|
58
|
+
puts "%s%s %i: %i records, %i bytes" % [
|
59
|
+
" " * depth,
|
60
|
+
page.level == 0 ? "LEAF" : "NODE",
|
61
|
+
page.offset,
|
62
|
+
page.page_header[:n_recs],
|
63
|
+
page.record_space,
|
64
|
+
]
|
65
|
+
if page.level == 0
|
66
|
+
page.each_record do |record|
|
67
|
+
puts "%sRECORD: (%s) -> (%s)" % [
|
68
|
+
" " * (depth+1),
|
69
|
+
record[:key].join(", "),
|
70
|
+
record[:row].join(", "),
|
71
|
+
]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end,
|
75
|
+
lambda do |parent_page, child_page, child_min_key, depth|
|
76
|
+
puts "%sLINK %i -> %i: %s records >= (%s)" % [
|
77
|
+
" " * depth,
|
78
|
+
parent_page.offset,
|
79
|
+
child_page.offset,
|
80
|
+
child_page.page_header[:n_recs],
|
81
|
+
child_min_key.join(", "),
|
82
|
+
]
|
83
|
+
end
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def index_digraph(index)
|
88
|
+
puts "digraph btree {"
|
89
|
+
puts " rankdir = LR;"
|
90
|
+
puts " ranksep = 2.0;"
|
91
|
+
index.recurse(
|
92
|
+
lambda do |page, depth|
|
93
|
+
label = "<page>Page %i|(%i records)" % [
|
94
|
+
page.offset,
|
95
|
+
page.page_header[:n_recs],
|
96
|
+
]
|
97
|
+
page.each_child_page do |child_page_number, child_key|
|
98
|
+
label += "|<dir_%i>(%s)" % [
|
99
|
+
child_page_number,
|
100
|
+
child_key.join(", "),
|
101
|
+
]
|
102
|
+
end
|
103
|
+
puts " %spage_%i [ shape = \"record\"; label = \"%s\"; ];" % [
|
104
|
+
" " * depth,
|
105
|
+
page.offset,
|
106
|
+
label,
|
107
|
+
]
|
108
|
+
end,
|
109
|
+
lambda do |parent_page, child_page, child_key, depth|
|
110
|
+
puts " %spage_%i:dir_%i -> page_%i:page:nw;" % [
|
111
|
+
" " * depth,
|
112
|
+
parent_page.offset,
|
113
|
+
child_page.offset,
|
114
|
+
child_page.offset,
|
115
|
+
]
|
116
|
+
end
|
117
|
+
)
|
118
|
+
puts "}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def index_level_summary(index, levels)
|
122
|
+
puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % [
|
123
|
+
"page",
|
124
|
+
"index",
|
125
|
+
"level",
|
126
|
+
"data",
|
127
|
+
"free",
|
128
|
+
"records",
|
129
|
+
"min_key",
|
130
|
+
]
|
131
|
+
|
132
|
+
levels.each do |level|
|
133
|
+
index.each_page_at_level(level) do |page|
|
134
|
+
puts "%-8i%-8i%-8i%-8i%-8i%-8i%s" % [
|
135
|
+
page.offset,
|
136
|
+
page.ph[:index_id],
|
137
|
+
page.ph[:level],
|
138
|
+
page.record_space,
|
139
|
+
page.free_space,
|
140
|
+
page.ph[:n_recs],
|
141
|
+
page.first_record[:key].join("|"),
|
142
|
+
]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def usage(exit_code, message = nil)
|
148
|
+
print "Error: #{message}\n" unless message.nil?
|
149
|
+
|
150
|
+
print <<'END_OF_USAGE'
|
151
|
+
|
152
|
+
Usage: innodb_space -f <file> [-p <page>] [-l <level>] <mode> [<mode>, ...]
|
153
|
+
|
154
|
+
--help, -?
|
155
|
+
Print this usage text.
|
156
|
+
|
157
|
+
--file, -f <file>
|
158
|
+
Load the tablespace file <file>.
|
159
|
+
|
160
|
+
--page, -p <page>
|
161
|
+
Operate on the page <page>; may be specified more than once.
|
162
|
+
|
163
|
+
--level, -l <level>
|
164
|
+
Operate on the level <level>; may be specified more than once.
|
165
|
+
|
166
|
+
--require, -r <file>
|
167
|
+
Use Ruby's "require" to load the file <file>. This is useful for loading
|
168
|
+
classes with record describers.
|
169
|
+
|
170
|
+
--describer, -d <describer>
|
171
|
+
Use the named record describer to parse records in index pages.
|
172
|
+
|
173
|
+
The following modes are supported:
|
174
|
+
|
175
|
+
page-dump
|
176
|
+
Dump the contents of the page, using the Ruby pp ("pretty-print") module.
|
177
|
+
|
178
|
+
space-summary
|
179
|
+
Summarize all pages within a tablespace.
|
180
|
+
|
181
|
+
space-index-pages-summary
|
182
|
+
Summarize all "INDEX" pages within a tablespace. This is useful to analyze
|
183
|
+
page fill rates and record counts per page. In addition to "INDEX" pages,
|
184
|
+
"ALLOCATED" pages are also printed and assumed to be completely empty.
|
185
|
+
|
186
|
+
index-recurse
|
187
|
+
Recurse an index, starting at the root (which must be provided in the first
|
188
|
+
--page/-p argument), printing the node pages, node pointers (links), leaf
|
189
|
+
pages. A record describer must be provided with the --describer/-d argument
|
190
|
+
to recurse indexes (in order to parse node pages).
|
191
|
+
|
192
|
+
index-digraph
|
193
|
+
Recurse an index as index-recurse does, but print a dot-compatible digraph
|
194
|
+
instead of a human-readable summary.
|
195
|
+
|
196
|
+
index-level-summary
|
197
|
+
Print a summary of all pages at a given level (provided with the --level/-l
|
198
|
+
argument) in an index.
|
199
|
+
|
200
|
+
END_OF_USAGE
|
201
|
+
|
202
|
+
exit exit_code
|
203
|
+
end
|
204
|
+
|
205
|
+
@options = OpenStruct.new
|
206
|
+
@options.file = nil
|
207
|
+
@options.pages = []
|
208
|
+
@options.levels = []
|
209
|
+
@options.describer = nil
|
210
|
+
|
211
|
+
getopt_options = [
|
212
|
+
[ "--help", "-?", GetoptLong::NO_ARGUMENT ],
|
213
|
+
[ "--file", "-f", GetoptLong::REQUIRED_ARGUMENT ],
|
214
|
+
[ "--page", "-p", GetoptLong::REQUIRED_ARGUMENT ],
|
215
|
+
[ "--level", "-l", GetoptLong::REQUIRED_ARGUMENT ],
|
216
|
+
[ "--require", "-r", GetoptLong::REQUIRED_ARGUMENT ],
|
217
|
+
[ "--describer", "-d", GetoptLong::REQUIRED_ARGUMENT ],
|
218
|
+
]
|
219
|
+
|
220
|
+
getopt = GetoptLong.new(*getopt_options)
|
221
|
+
|
222
|
+
getopt.each do |opt, arg|
|
223
|
+
case opt
|
224
|
+
when "--help"
|
225
|
+
usage 0
|
226
|
+
when "--mode"
|
227
|
+
@options.mode = arg
|
228
|
+
when "--file"
|
229
|
+
@options.file = arg
|
230
|
+
when "--page"
|
231
|
+
@options.pages << arg.to_i
|
232
|
+
when "--level"
|
233
|
+
@options.levels << arg.to_i
|
234
|
+
when "--require"
|
235
|
+
require arg
|
236
|
+
when "--describer"
|
237
|
+
@options.describer = arg
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
unless @options.file
|
242
|
+
usage 1, "File must be provided with -f argument"
|
243
|
+
end
|
244
|
+
|
245
|
+
space = Innodb::Space.new(@options.file)
|
246
|
+
|
247
|
+
if @options.describer
|
248
|
+
space.record_describer = Innodb::RecordDescriber.const_get(@options.describer)
|
249
|
+
end
|
250
|
+
|
251
|
+
if ARGV.empty?
|
252
|
+
usage 1, "At least one mode should be provided"
|
253
|
+
end
|
254
|
+
|
255
|
+
ARGV.each do |mode|
|
256
|
+
case mode
|
257
|
+
when "page-dump"
|
258
|
+
if @options.pages.empty?
|
259
|
+
usage 1, "Page numbers to dump must be provided"
|
260
|
+
end
|
261
|
+
|
262
|
+
@options.pages.each do |page|
|
263
|
+
space.page(page).dump
|
264
|
+
end
|
265
|
+
when "space-summary"
|
266
|
+
space_summary(space)
|
267
|
+
when "space-index-pages-summary"
|
268
|
+
space_index_pages_summary(space)
|
269
|
+
when "index-recurse"
|
270
|
+
unless space.record_describer
|
271
|
+
usage 1, "Record describer necessary for index recursion"
|
272
|
+
end
|
273
|
+
|
274
|
+
if @options.pages.empty?
|
275
|
+
usage 1, "Page number of index root must be provided"
|
276
|
+
end
|
277
|
+
|
278
|
+
@options.pages.each do |page|
|
279
|
+
index_recurse(space.index(page))
|
280
|
+
end
|
281
|
+
when "index-digraph"
|
282
|
+
unless space.record_describer
|
283
|
+
usage 1, "Record describer necessary for index recursion"
|
284
|
+
end
|
285
|
+
|
286
|
+
if @options.pages.empty?
|
287
|
+
usage 1, "Page number of index root must be provided"
|
288
|
+
end
|
289
|
+
|
290
|
+
@options.pages.each do |page|
|
291
|
+
index_digraph(space.index(page))
|
292
|
+
end
|
293
|
+
when "index-level-summary"
|
294
|
+
unless space.record_describer
|
295
|
+
usage 1, "Record describer necessary for index recursion"
|
296
|
+
end
|
297
|
+
|
298
|
+
if @options.pages.empty?
|
299
|
+
usage 1, "Page number of index root must be provided"
|
300
|
+
end
|
301
|
+
|
302
|
+
index_level_summary(space.index(@options.pages.first), @options.levels)
|
303
|
+
else
|
304
|
+
usage 1, "Unknown mode: #{mode}"
|
305
|
+
end
|
306
|
+
end
|
data/lib/innodb.rb
CHANGED
data/lib/innodb/index.rb
CHANGED
@@ -32,7 +32,7 @@ class Innodb::Index
|
|
32
32
|
|
33
33
|
parent_page.each_child_page do |child_page_number, child_min_key|
|
34
34
|
child_page = @space.page(child_page_number)
|
35
|
-
child_page.
|
35
|
+
child_page.record_describer = @space.record_describer
|
36
36
|
if child_page.type == :INDEX
|
37
37
|
if link_proc
|
38
38
|
link_proc.call(parent_page, child_page, child_min_key, depth+1)
|
@@ -76,7 +76,7 @@ class Innodb::Index
|
|
76
76
|
|
77
77
|
# Iterate through all records on all leaf pages in ascending order.
|
78
78
|
def each_record
|
79
|
-
|
79
|
+
each_page_at_level(0) do |page|
|
80
80
|
page.each_record do |record|
|
81
81
|
yield record
|
82
82
|
end
|
data/lib/innodb/log.rb
CHANGED
@@ -16,10 +16,14 @@ class Innodb::Log
|
|
16
16
|
|
17
17
|
# Open a log file.
|
18
18
|
def initialize(file)
|
19
|
-
@
|
20
|
-
@
|
21
|
-
|
19
|
+
@name = file
|
20
|
+
File.open(@name) do |log|
|
21
|
+
@size = log.stat.size
|
22
|
+
@blocks = ((@size - DATA_START) / Innodb::LogBlock::BLOCK_SIZE)
|
23
|
+
end
|
22
24
|
end
|
25
|
+
|
26
|
+
attr_reader :blocks
|
23
27
|
|
24
28
|
# Return a log block with a given block number as an InnoDB::LogBlock object.
|
25
29
|
# Blocks are numbered after the log file header, starting from 0.
|
@@ -27,9 +31,11 @@ class Innodb::Log
|
|
27
31
|
offset = DATA_START + (block_number.to_i * Innodb::LogBlock::BLOCK_SIZE)
|
28
32
|
return nil unless offset < @size
|
29
33
|
return nil unless (offset + Innodb::LogBlock::BLOCK_SIZE) <= @size
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
File.open(@name) do |log|
|
35
|
+
log.seek(offset)
|
36
|
+
block_data = log.read(Innodb::LogBlock::BLOCK_SIZE)
|
37
|
+
Innodb::LogBlock.new(block_data)
|
38
|
+
end
|
33
39
|
end
|
34
40
|
|
35
41
|
# Iterate through all log blocks, returning the block number and an
|
data/lib/innodb/log_block.rb
CHANGED
@@ -57,7 +57,7 @@ class Innodb::LogBlock
|
|
57
57
|
@header ||= begin
|
58
58
|
c = cursor(HEADER_START)
|
59
59
|
{
|
60
|
-
:block => c.get_uint32,
|
60
|
+
:block => c.get_uint32 & (2**31-1),
|
61
61
|
:data_length => c.get_uint16,
|
62
62
|
:first_rec_group => c.get_uint16,
|
63
63
|
:checkpoint_no => c.get_uint32,
|
@@ -143,7 +143,7 @@ class Innodb::LogBlock
|
|
143
143
|
end
|
144
144
|
|
145
145
|
SINGLE_RECORD_MASK = 0x80
|
146
|
-
RECORD_TYPE_MASK
|
146
|
+
RECORD_TYPE_MASK = 0x7f
|
147
147
|
|
148
148
|
# Return the log record. (This is mostly unimplemented.)
|
149
149
|
def record
|
@@ -153,7 +153,7 @@ class Innodb::LogBlock
|
|
153
153
|
type_and_flag = c.get_uint8
|
154
154
|
type = type_and_flag & RECORD_TYPE_MASK
|
155
155
|
type = RECORD_TYPES[type] || type
|
156
|
-
single_record = (type_and_flag & SINGLE_RECORD_MASK)
|
156
|
+
single_record = (type_and_flag & SINGLE_RECORD_MASK) > 0
|
157
157
|
{
|
158
158
|
:type => type,
|
159
159
|
:single_record => single_record,
|
data/lib/innodb/page.rb
CHANGED
@@ -126,13 +126,18 @@ class Innodb::Page
|
|
126
126
|
fil_header[:next]
|
127
127
|
end
|
128
128
|
|
129
|
+
# A helper function to return the LSN, for easier access.
|
130
|
+
def lsn
|
131
|
+
fil_header[:lsn]
|
132
|
+
end
|
133
|
+
|
129
134
|
# Dump the contents of a page for debugging purposes.
|
130
135
|
def dump
|
131
|
-
puts
|
132
136
|
puts "#{self}:"
|
133
|
-
|
134
137
|
puts
|
138
|
+
|
135
139
|
puts "fil header:"
|
136
140
|
pp fil_header
|
141
|
+
puts
|
137
142
|
end
|
138
143
|
end
|
data/lib/innodb/page/index.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Innodb::Page::Index < Innodb::Page
|
2
|
-
attr_accessor :
|
2
|
+
attr_accessor :record_describer
|
3
3
|
|
4
4
|
# Return the byte offset of the start of the "index" page header, which
|
5
5
|
# immediately follows the "fil" header.
|
@@ -229,8 +229,8 @@ class Innodb::Page::Index < Innodb::Page
|
|
229
229
|
|
230
230
|
# Return (and cache) the record format provided by an external class.
|
231
231
|
def record_format
|
232
|
-
if
|
233
|
-
@record_format ||=
|
232
|
+
if record_describer
|
233
|
+
@record_format ||= record_describer.cursor_sendable_description(self)
|
234
234
|
end
|
235
235
|
end
|
236
236
|
|
@@ -318,11 +318,10 @@ class Innodb::Page::Index < Innodb::Page
|
|
318
318
|
def dump
|
319
319
|
super
|
320
320
|
|
321
|
-
puts
|
322
321
|
puts "page header:"
|
323
322
|
pp page_header
|
324
|
-
|
325
323
|
puts
|
324
|
+
|
326
325
|
puts "sizes:"
|
327
326
|
puts " %-15s%5i" % [ "header", header_space ]
|
328
327
|
puts " %-15s%5i" % [ "trailer", trailer_space ]
|
@@ -334,17 +333,18 @@ class Innodb::Page::Index < Innodb::Page
|
|
334
333
|
"per record",
|
335
334
|
(page_header[:n_recs] > 0) ? (record_space / page_header[:n_recs]) : 0
|
336
335
|
]
|
337
|
-
|
338
336
|
puts
|
337
|
+
|
339
338
|
puts "system records:"
|
340
339
|
pp infimum
|
341
340
|
pp supremum
|
342
|
-
|
343
341
|
puts
|
342
|
+
|
344
343
|
puts "records:"
|
345
344
|
each_record do |rec|
|
346
345
|
pp rec
|
347
346
|
end
|
347
|
+
puts
|
348
348
|
end
|
349
349
|
end
|
350
350
|
|
data/lib/innodb/page/inode.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
class Innodb::RecordDescriber; end
|
data/lib/innodb/space.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# An InnoDB tablespace file, which can be either a multi-table ibdataN file
|
2
2
|
# or a single-table "innodb_file_per_table" .ibd file.
|
3
3
|
class Innodb::Space
|
4
|
-
attr_accessor :
|
4
|
+
attr_accessor :record_describer
|
5
5
|
|
6
6
|
# Currently only 16kB InnoDB pages are supported.
|
7
7
|
PAGE_SIZE = 16384
|
@@ -11,7 +11,7 @@ class Innodb::Space
|
|
11
11
|
@file = File.open(file)
|
12
12
|
@size = @file.stat.size
|
13
13
|
@pages = (@size / PAGE_SIZE)
|
14
|
-
@
|
14
|
+
@record_describer = nil
|
15
15
|
end
|
16
16
|
|
17
17
|
# Get an Innodb::Page object for a specific page by page number.
|
@@ -24,12 +24,17 @@ class Innodb::Space
|
|
24
24
|
this_page = Innodb::Page.parse(page_data)
|
25
25
|
|
26
26
|
if this_page.type == :INDEX
|
27
|
-
this_page.
|
27
|
+
this_page.record_describer = @record_describer
|
28
28
|
end
|
29
29
|
|
30
30
|
this_page
|
31
31
|
end
|
32
32
|
|
33
|
+
# Get an Innodb::Index object for a specific index by root page number.
|
34
|
+
def index(root_page_number)
|
35
|
+
Innodb::Index.new(self, root_page_number)
|
36
|
+
end
|
37
|
+
|
33
38
|
# Iterate through all pages in a tablespace, returning the page number
|
34
39
|
# and an Innodb::Page object for each one.
|
35
40
|
def each_page
|
data/lib/innodb/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: innodb_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Cole
|
@@ -15,14 +15,14 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-12-01 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: Library for parsing InnoDB data files in Ruby
|
22
22
|
email: jeremy@jcole.us
|
23
23
|
executables:
|
24
|
-
-
|
25
|
-
-
|
24
|
+
- innodb_log
|
25
|
+
- innodb_space
|
26
26
|
extensions: []
|
27
27
|
|
28
28
|
extra_rdoc_files: []
|
@@ -38,10 +38,11 @@ files:
|
|
38
38
|
- lib/innodb/page/fsp_hdr_xdes.rb
|
39
39
|
- lib/innodb/page/index.rb
|
40
40
|
- lib/innodb/page/inode.rb
|
41
|
+
- lib/innodb/record_describer.rb
|
41
42
|
- lib/innodb/space.rb
|
42
43
|
- lib/innodb/version.rb
|
43
|
-
- bin/
|
44
|
-
- bin/
|
44
|
+
- bin/innodb_log
|
45
|
+
- bin/innodb_space
|
45
46
|
homepage: http://jcole.us/
|
46
47
|
licenses: []
|
47
48
|
|
data/bin/innodb_dump_space
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "pp"
|
4
|
-
require "innodb"
|
5
|
-
|
6
|
-
class EdgesRecordFormatter
|
7
|
-
def format(page)
|
8
|
-
bytes_per_record = (page.record_space / page.page_header[:n_recs])
|
9
|
-
case
|
10
|
-
when [48, 26].include?(bytes_per_record) # Clustered Key
|
11
|
-
{
|
12
|
-
# PRIMARY KEY (source_id, state, position)
|
13
|
-
:type => :clustered,
|
14
|
-
:key => [
|
15
|
-
[:get_uint64], # source_id
|
16
|
-
[:get_i_sint8], # state
|
17
|
-
[:get_i_sint64], # position
|
18
|
-
],
|
19
|
-
:row => [
|
20
|
-
[:get_uint32], # updated_at
|
21
|
-
[:get_uint64], # destination_id
|
22
|
-
[:get_uint8], # count
|
23
|
-
],
|
24
|
-
}
|
25
|
-
when [30, 34].include?(bytes_per_record) # Secondary Key
|
26
|
-
{
|
27
|
-
# INDEX (source_id, destination_id)
|
28
|
-
:type => :secondary,
|
29
|
-
:key => [
|
30
|
-
[:get_uint64], # source_id
|
31
|
-
[:get_uint64], # destination_id
|
32
|
-
],
|
33
|
-
# PKV ([source_id], state, position)
|
34
|
-
:row => [
|
35
|
-
[:get_i_sint8], # state
|
36
|
-
[:get_i_sint64], # position
|
37
|
-
],
|
38
|
-
}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class PageSplitTestFormatter
|
44
|
-
def format(page)
|
45
|
-
{
|
46
|
-
# PRIMARY KEY (id)
|
47
|
-
:type => :clustered,
|
48
|
-
:key => [
|
49
|
-
[:get_uint64], # id
|
50
|
-
],
|
51
|
-
:row => [],
|
52
|
-
}
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def digraph_index(index)
|
57
|
-
puts "digraph btree {"
|
58
|
-
puts " rankdir = LR;"
|
59
|
-
puts " ranksep = 2.0;"
|
60
|
-
index.recurse(
|
61
|
-
lambda do |page, depth|
|
62
|
-
if true #page.level != 0
|
63
|
-
label = "<page>Page %i|(%i records)" % [
|
64
|
-
page.offset,
|
65
|
-
page.page_header[:n_recs],
|
66
|
-
]
|
67
|
-
page.each_child_page do |child_page_number, child_key|
|
68
|
-
label += "|<dir_%i>(%s)" % [
|
69
|
-
child_page_number,
|
70
|
-
child_key.join(", "),
|
71
|
-
]
|
72
|
-
end
|
73
|
-
puts " %spage_%i [ shape = \"record\"; label = \"%s\"; ];" % [
|
74
|
-
" " * depth,
|
75
|
-
page.offset,
|
76
|
-
label,
|
77
|
-
]
|
78
|
-
end
|
79
|
-
end,
|
80
|
-
lambda do |parent_page, child_page, child_key, depth|
|
81
|
-
if true #child_page.level > 0
|
82
|
-
puts " %spage_%i:dir_%i -> page_%i:page:nw;" % [
|
83
|
-
" " * depth,
|
84
|
-
parent_page.offset,
|
85
|
-
child_page.offset,
|
86
|
-
child_page.offset,
|
87
|
-
]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
)
|
91
|
-
puts "}"
|
92
|
-
end
|
93
|
-
|
94
|
-
def recurse_index(index)
|
95
|
-
index.recurse(
|
96
|
-
lambda do |page, depth|
|
97
|
-
puts "%s%s %i: %i records, %i bytes" % [
|
98
|
-
" " * depth,
|
99
|
-
page.level == 0 ? "LEAF" : "NODE",
|
100
|
-
page.offset,
|
101
|
-
page.page_header[:n_recs],
|
102
|
-
page.record_space,
|
103
|
-
]
|
104
|
-
if page.level == 0
|
105
|
-
page.each_record do |record|
|
106
|
-
puts "%sRECORD: (%s) -> (%s)" % [
|
107
|
-
" " * (depth+1),
|
108
|
-
record[:key].join(", "),
|
109
|
-
record[:row].join(", "),
|
110
|
-
]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end,
|
114
|
-
lambda do |parent_page, child_page, child_min_key, depth|
|
115
|
-
puts "%sLINK %i -> %i: %s records >= (%s)" % [
|
116
|
-
" " * depth,
|
117
|
-
parent_page.offset,
|
118
|
-
child_page.offset,
|
119
|
-
child_page.page_header[:n_recs],
|
120
|
-
child_min_key.join(", "),
|
121
|
-
]
|
122
|
-
end
|
123
|
-
)
|
124
|
-
end
|
125
|
-
|
126
|
-
def dump_space(space)
|
127
|
-
puts "%-8s%-8s%-8s%-8s%-8s%-8s" % [
|
128
|
-
"page",
|
129
|
-
"index",
|
130
|
-
"level",
|
131
|
-
"data",
|
132
|
-
"free",
|
133
|
-
"records",
|
134
|
-
]
|
135
|
-
|
136
|
-
space.each_page do |page_number, page|
|
137
|
-
case page.type
|
138
|
-
when :INDEX
|
139
|
-
puts "%-8i%-8i%-8i%-8i%-8i%-8i" % [
|
140
|
-
page_number,
|
141
|
-
page.ph[:index_id],
|
142
|
-
page.ph[:level],
|
143
|
-
page.record_space,
|
144
|
-
page.free_space,
|
145
|
-
page.ph[:n_recs],
|
146
|
-
]
|
147
|
-
when :ALLOCATED
|
148
|
-
puts "%-8i%-8i%-8i%-8i%-8i%-8i" % [ page_number, 0, 0, 0, Innodb::Space::PAGE_SIZE, 0 ]
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def dump_index_level(index, level)
|
154
|
-
puts "%-8s%-8s%-8s%-8s%-8s%-8s%-8s" % [
|
155
|
-
"page",
|
156
|
-
"index",
|
157
|
-
"level",
|
158
|
-
"data",
|
159
|
-
"free",
|
160
|
-
"records",
|
161
|
-
"min_key",
|
162
|
-
]
|
163
|
-
|
164
|
-
index.each_page_at_level(level) do |page|
|
165
|
-
puts "%-8i%-8i%-8i%-8i%-8i%-8i%s" % [
|
166
|
-
page.offset,
|
167
|
-
page.ph[:index_id],
|
168
|
-
page.ph[:level],
|
169
|
-
page.record_space,
|
170
|
-
page.free_space,
|
171
|
-
page.ph[:n_recs],
|
172
|
-
page.first_record[:key].join("|"),
|
173
|
-
]
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
file, page_number = ARGV.shift(2)
|
178
|
-
|
179
|
-
formatter = EdgesRecordFormatter.new
|
180
|
-
#formatter = PageSplitTestFormatter.new
|
181
|
-
space = Innodb::Space.new(file)
|
182
|
-
space.record_formatter = formatter
|
183
|
-
space.page(page_number).dump
|
184
|
-
#space.each_page { |page_number, page| puts "%6i%20s" % [page_number, page.type] }
|
185
|
-
#dump_space(space)
|
186
|
-
#dump_index_level(Innodb::Index.new(space, page_number), 1)
|
187
|
-
#recurse_index(Innodb::Index.new(space, page_number))
|
188
|
-
#digraph_index(Innodb::Index.new(space, page_number))
|
189
|
-
#Innodb::Index.new(space, page_number).each_record { |record| puts "(#{record[:key].join(", ")}) -> (#{record[:row].join(", ")})" }
|