innodb_ruby 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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(", ")})" }
|