innodb_ruby 0.6.4 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/innodb_space +12 -14
- data/lib/innodb.rb +1 -0
- data/lib/innodb/fseg_entry.rb +11 -0
- data/lib/innodb/index.rb +66 -6
- data/lib/innodb/page.rb +0 -1
- data/lib/innodb/page/index.rb +73 -20
- data/lib/innodb/page/trx_sys.rb +76 -0
- data/lib/innodb/version.rb +1 -1
- metadata +6 -4
data/bin/innodb_space
CHANGED
@@ -40,11 +40,11 @@ def space_index_pages_summary(space)
|
|
40
40
|
when :INDEX
|
41
41
|
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [
|
42
42
|
page_number,
|
43
|
-
page.
|
44
|
-
page.
|
43
|
+
page.page_header[:index_id],
|
44
|
+
page.level,
|
45
45
|
page.record_space,
|
46
46
|
page.free_space,
|
47
|
-
page.
|
47
|
+
page.records,
|
48
48
|
]
|
49
49
|
when :ALLOCATED
|
50
50
|
puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [ page_number, 0, 0, 0, page.size, 0 ]
|
@@ -119,11 +119,11 @@ end
|
|
119
119
|
def index_recurse(index)
|
120
120
|
index.recurse(
|
121
121
|
lambda do |page, depth|
|
122
|
-
puts "%s%s
|
122
|
+
puts "%s%s NODE #%i: %i records, %i bytes" % [
|
123
123
|
" " * depth,
|
124
|
-
page.
|
124
|
+
index.node_type(page).to_s.upcase,
|
125
125
|
page.offset,
|
126
|
-
page.
|
126
|
+
page.records,
|
127
127
|
page.record_space,
|
128
128
|
]
|
129
129
|
if page.level == 0
|
@@ -137,12 +137,10 @@ def index_recurse(index)
|
|
137
137
|
end
|
138
138
|
end,
|
139
139
|
lambda do |parent_page, child_page, child_min_key, depth|
|
140
|
-
puts "%
|
140
|
+
puts "%sNODE POINTER RECORD >= (%s) -> #%i" % [
|
141
141
|
" " * depth,
|
142
|
-
parent_page.offset,
|
143
|
-
child_page.offset,
|
144
|
-
child_page.page_header[:n_recs],
|
145
142
|
child_min_key.join(", "),
|
143
|
+
child_page.offset,
|
146
144
|
]
|
147
145
|
end
|
148
146
|
)
|
@@ -156,7 +154,7 @@ def index_digraph(index)
|
|
156
154
|
lambda do |page, depth|
|
157
155
|
label = "<page>Page %i|(%i records)" % [
|
158
156
|
page.offset,
|
159
|
-
page.
|
157
|
+
page.records,
|
160
158
|
]
|
161
159
|
page.each_child_page do |child_page_number, child_key|
|
162
160
|
label += "|<dir_%i>(%s)" % [
|
@@ -197,11 +195,11 @@ def index_level_summary(index, levels)
|
|
197
195
|
index.each_page_at_level(level) do |page|
|
198
196
|
puts "%-8i%-8i%-8i%-8i%-8i%-8i%s" % [
|
199
197
|
page.offset,
|
200
|
-
page.
|
201
|
-
page.
|
198
|
+
page.page_header[:index_id],
|
199
|
+
page.level,
|
202
200
|
page.record_space,
|
203
201
|
page.free_space,
|
204
|
-
page.
|
202
|
+
page.records,
|
205
203
|
page.first_record[:key].join("|"),
|
206
204
|
]
|
207
205
|
end
|
data/lib/innodb.rb
CHANGED
data/lib/innodb/index.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# An InnoDB index B-tree, given an Innodb::Space and a root page number.
|
2
2
|
class Innodb::Index
|
3
|
+
attr_reader :root
|
4
|
+
|
3
5
|
def initialize(space, root_page_number)
|
4
6
|
@space = space
|
5
7
|
@root = @space.page(root_page_number)
|
@@ -13,11 +15,6 @@ class Innodb::Index
|
|
13
15
|
raise "Page #{root_page_number} is a #{@root.type} page, not an INDEX page"
|
14
16
|
end
|
15
17
|
|
16
|
-
# The root page should not be a leaf page.
|
17
|
-
unless @root.level > 0
|
18
|
-
raise "Page #{root_page_number} is a leaf page"
|
19
|
-
end
|
20
|
-
|
21
18
|
# The root page should be the only page at its level.
|
22
19
|
unless @root.prev.nil? && @root.next.nil?
|
23
20
|
raise "Page #{root_page_number} is a node page, but not appear to be the root; it has previous page and next page pointers"
|
@@ -29,6 +26,17 @@ class Innodb::Index
|
|
29
26
|
@root.page_header[:index_id]
|
30
27
|
end
|
31
28
|
|
29
|
+
# Return the type of node that the given page represents in the index tree.
|
30
|
+
def node_type(page)
|
31
|
+
if @root.offset == page.offset
|
32
|
+
:root
|
33
|
+
elsif page.level == 0
|
34
|
+
:leaf
|
35
|
+
else
|
36
|
+
:internal
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
32
40
|
# Internal method used by recurse.
|
33
41
|
def _recurse(parent_page, page_proc, link_proc, depth=0)
|
34
42
|
if page_proc && parent_page.type == :INDEX
|
@@ -87,4 +95,56 @@ class Innodb::Index
|
|
87
95
|
end
|
88
96
|
end
|
89
97
|
end
|
90
|
-
|
98
|
+
|
99
|
+
# Compare two arrays of fields to determine if they are equal. This follows
|
100
|
+
# the same comparison rules as strcmp and others:
|
101
|
+
# 0 = a is equal to b
|
102
|
+
# -1 = a is less than b
|
103
|
+
# +1 = a is greater than b
|
104
|
+
def compare_key(a, b)
|
105
|
+
return -1 if a.size < b.size
|
106
|
+
return +1 if a.size > b.size
|
107
|
+
a.each_index do |i|
|
108
|
+
return -1 if a[i] < b[i]
|
109
|
+
return +1 if a[i] > b[i]
|
110
|
+
end
|
111
|
+
return 0
|
112
|
+
end
|
113
|
+
|
114
|
+
# Search for a record within a single page, and return either a perfect
|
115
|
+
# match for the key, or the last record closest to they key but not greater
|
116
|
+
# than the key. (If an exact match is desired, compare_key must be used to
|
117
|
+
# check if the returned record matches. This makes the function useful for
|
118
|
+
# search in both leaf and non-leaf pages.)
|
119
|
+
def linear_search_in_page(page, key)
|
120
|
+
c = page.record_cursor(page.infimum[:next])
|
121
|
+
this_rec = c.record
|
122
|
+
while next_rec = c.record
|
123
|
+
return this_rec if next_rec == page.supremum
|
124
|
+
if (compare_key(key, this_rec[:key]) >= 0) &&
|
125
|
+
(compare_key(key, next_rec[:key]) < 0)
|
126
|
+
return this_rec
|
127
|
+
end
|
128
|
+
this_rec = next_rec
|
129
|
+
end
|
130
|
+
this_rec
|
131
|
+
end
|
132
|
+
|
133
|
+
# Search for a record within the entire index, walking down the non-leaf
|
134
|
+
# pages until a leaf page is found, and then verifying that the record
|
135
|
+
# returned on the leaf page is an exact match for the key. If a matching
|
136
|
+
# record is not found, nil is returned (either because linear_search_in_page
|
137
|
+
# returns nil breaking the loop, or because compare_key returns non-zero).
|
138
|
+
def linear_search(key)
|
139
|
+
page = @root
|
140
|
+
|
141
|
+
while rec = linear_search_in_page(page, key)
|
142
|
+
if page.level > 0
|
143
|
+
page = @space.page(rec[:child_page_number])
|
144
|
+
else
|
145
|
+
return page, rec if compare_key(key, rec[:key]) == 0
|
146
|
+
break
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/innodb/page.rb
CHANGED
data/lib/innodb/page/index.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "innodb/fseg_entry"
|
2
|
+
|
1
3
|
class Innodb::Page::Index < Innodb::Page
|
2
4
|
attr_accessor :record_describer
|
3
5
|
|
@@ -20,7 +22,7 @@ class Innodb::Page::Index < Innodb::Page
|
|
20
22
|
|
21
23
|
# The size of the "fseg" header.
|
22
24
|
def size_fseg_header
|
23
|
-
2 *
|
25
|
+
2 * Innodb::FsegEntry::SIZE
|
24
26
|
end
|
25
27
|
|
26
28
|
# Return the byte offset of the start of records within the page (the
|
@@ -69,9 +71,14 @@ class Innodb::Page::Index < Innodb::Page
|
|
69
71
|
pos_user_records
|
70
72
|
end
|
71
73
|
|
74
|
+
# The number of directory slots in use.
|
75
|
+
def directory_slots
|
76
|
+
page_header[:n_dir_slots]
|
77
|
+
end
|
78
|
+
|
72
79
|
# The amount of space consumed by the page directory.
|
73
80
|
def directory_space
|
74
|
-
|
81
|
+
directory_slots * PAGE_DIR_SLOT_SIZE
|
75
82
|
end
|
76
83
|
|
77
84
|
# The amount of space consumed by the trailers in the page.
|
@@ -129,7 +136,6 @@ class Innodb::Page::Index < Innodb::Page
|
|
129
136
|
:format => (n_heap & 1<<15) == 0 ? :redundant : :compact,
|
130
137
|
}
|
131
138
|
end
|
132
|
-
alias :ph :page_header
|
133
139
|
|
134
140
|
# A helper function to return the page level from the "page" header, for
|
135
141
|
# easier access.
|
@@ -137,27 +143,23 @@ class Innodb::Page::Index < Innodb::Page
|
|
137
143
|
page_header && page_header[:level]
|
138
144
|
end
|
139
145
|
|
146
|
+
# A helper function to return the number of records.
|
147
|
+
def records
|
148
|
+
page_header && page_header[:n_recs]
|
149
|
+
end
|
150
|
+
|
140
151
|
# A helper function to identify root index pages; they must be the only pages
|
141
152
|
# at their level.
|
142
153
|
def root?
|
143
154
|
self.prev.nil? && self.next.nil?
|
144
155
|
end
|
145
156
|
|
146
|
-
# Parse an "fseg" header entry.
|
147
|
-
def fseg_entry(cursor)
|
148
|
-
{
|
149
|
-
:space_id => cursor.get_uint32,
|
150
|
-
:page_number => cursor.get_uint32,
|
151
|
-
:offset => cursor.get_uint16,
|
152
|
-
}
|
153
|
-
end
|
154
|
-
|
155
157
|
# Return the "fseg" header.
|
156
158
|
def fseg_header
|
157
159
|
c = cursor(pos_fseg_header)
|
158
160
|
@fseg_header ||= {
|
159
|
-
:free_list =>
|
160
|
-
:btree_segment =>
|
161
|
+
:free_list => Innodb::FsegEntry.get_entry(c),
|
162
|
+
:btree_segment => Innodb::FsegEntry.get_entry(c),
|
161
163
|
}
|
162
164
|
end
|
163
165
|
|
@@ -260,8 +262,8 @@ class Innodb::Page::Index < Innodb::Page
|
|
260
262
|
def record(offset)
|
261
263
|
return nil unless offset
|
262
264
|
return nil unless type == :INDEX
|
263
|
-
return
|
264
|
-
return
|
265
|
+
return infimum if offset == pos_infimum
|
266
|
+
return supremum if offset == pos_supremum
|
265
267
|
|
266
268
|
c = cursor(offset).forward
|
267
269
|
|
@@ -269,6 +271,7 @@ class Innodb::Page::Index < Innodb::Page
|
|
269
271
|
header = record_header(offset)
|
270
272
|
|
271
273
|
this_record = {
|
274
|
+
:offset => offset,
|
272
275
|
:header => header,
|
273
276
|
:next => header[:next] == 0 ? nil : (offset + header[:next]),
|
274
277
|
}
|
@@ -311,17 +314,48 @@ class Innodb::Page::Index < Innodb::Page
|
|
311
314
|
this_record
|
312
315
|
end
|
313
316
|
|
317
|
+
# A class for cursoring through records starting from an arbitrary point.
|
318
|
+
class RecordCursor
|
319
|
+
def initialize(page, offset)
|
320
|
+
@page = page
|
321
|
+
@offset = offset
|
322
|
+
end
|
323
|
+
|
324
|
+
# Return the next record, and advance the cursor. Return nil when the
|
325
|
+
# end of records is reached.
|
326
|
+
def record
|
327
|
+
return nil unless @offset
|
328
|
+
|
329
|
+
record = @page.record(@offset)
|
330
|
+
|
331
|
+
if record == @page.supremum
|
332
|
+
@offset = nil
|
333
|
+
else
|
334
|
+
@offset = record[:next]
|
335
|
+
record
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
# Return a RecordCursor starting at offset.
|
341
|
+
def record_cursor(offset)
|
342
|
+
RecordCursor.new(self, offset)
|
343
|
+
end
|
344
|
+
|
314
345
|
# Return the first record on this page.
|
315
346
|
def first_record
|
316
|
-
record(infimum[:next])
|
347
|
+
first = record(infimum[:next])
|
348
|
+
first if first != supremum
|
317
349
|
end
|
318
350
|
|
319
|
-
# Iterate through all records.
|
351
|
+
# Iterate through all records.
|
320
352
|
def each_record
|
321
|
-
|
322
|
-
|
353
|
+
c = record_cursor(infimum[:next])
|
354
|
+
|
355
|
+
while rec = c.record
|
323
356
|
yield rec
|
324
357
|
end
|
358
|
+
|
325
359
|
nil
|
326
360
|
end
|
327
361
|
|
@@ -330,12 +364,27 @@ class Innodb::Page::Index < Innodb::Page
|
|
330
364
|
# record.
|
331
365
|
def each_child_page
|
332
366
|
return nil if level == 0
|
367
|
+
|
333
368
|
each_record do |rec|
|
334
369
|
yield rec[:child_page_number], rec[:key]
|
335
370
|
end
|
371
|
+
|
336
372
|
nil
|
337
373
|
end
|
338
374
|
|
375
|
+
# Return an array of row offsets for all entries in the page directory.
|
376
|
+
def directory
|
377
|
+
return @directory if @directory
|
378
|
+
|
379
|
+
@directory = []
|
380
|
+
c = cursor(pos_directory).backward
|
381
|
+
directory_slots.times do
|
382
|
+
@directory.push c.get_uint16
|
383
|
+
end
|
384
|
+
|
385
|
+
@directory
|
386
|
+
end
|
387
|
+
|
339
388
|
# Dump the contents of a page for debugging purposes.
|
340
389
|
def dump
|
341
390
|
super
|
@@ -366,6 +415,10 @@ class Innodb::Page::Index < Innodb::Page
|
|
366
415
|
pp supremum
|
367
416
|
puts
|
368
417
|
|
418
|
+
puts "page directory:"
|
419
|
+
pp directory
|
420
|
+
puts
|
421
|
+
|
369
422
|
puts "records:"
|
370
423
|
each_record do |rec|
|
371
424
|
pp rec
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class Innodb::Page::TrxSys < Innodb::Page
|
2
|
+
def pos_trx_sys_header
|
3
|
+
pos_fil_header + size_fil_header
|
4
|
+
end
|
5
|
+
|
6
|
+
def pos_mysql_binary_log_info
|
7
|
+
size - 1000
|
8
|
+
end
|
9
|
+
|
10
|
+
def pos_mysql_master_log_info
|
11
|
+
size - 2000
|
12
|
+
end
|
13
|
+
|
14
|
+
def pos_doublewrite_info
|
15
|
+
size - 200
|
16
|
+
end
|
17
|
+
|
18
|
+
MYSQL_LOG_MAGIC_N = 873422344
|
19
|
+
|
20
|
+
def mysql_log_info(offset)
|
21
|
+
c = cursor(offset)
|
22
|
+
if c.get_uint32 == MYSQL_LOG_MAGIC_N
|
23
|
+
{
|
24
|
+
:offset => c.get_uint64,
|
25
|
+
:name => c.get_bytes(100),
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
DOUBLEWRITE_MAGIC_N = 536853855
|
31
|
+
|
32
|
+
def doublewrite_page_info(cursor)
|
33
|
+
{
|
34
|
+
:magic_n => cursor.get_uint32,
|
35
|
+
:page_number => [
|
36
|
+
cursor.get_uint32,
|
37
|
+
cursor.get_uint32,
|
38
|
+
],
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N = 1783657386
|
43
|
+
|
44
|
+
def doublewrite_info
|
45
|
+
c = cursor(pos_doublewrite_info)
|
46
|
+
{
|
47
|
+
:fseg => Innodb::FsegEntry.get_entry(c),
|
48
|
+
:page_info => [
|
49
|
+
doublewrite_page_info(c),
|
50
|
+
doublewrite_page_info(c),
|
51
|
+
],
|
52
|
+
:space_id_stored => c.get_uint32 == DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N,
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def trx_sys
|
57
|
+
c = cursor(pos_trx_sys_header)
|
58
|
+
@trx_sys ||= {
|
59
|
+
:trx_id => c.get_uint64,
|
60
|
+
:fseg => Innodb::FsegEntry.get_entry(c),
|
61
|
+
:binary_log => mysql_log_info(pos_mysql_binary_log_info),
|
62
|
+
:master_log => mysql_log_info(pos_mysql_master_log_info),
|
63
|
+
:doublewrite => doublewrite_info,
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def dump
|
68
|
+
super
|
69
|
+
|
70
|
+
puts "trx_sys:"
|
71
|
+
pp trx_sys
|
72
|
+
puts
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
|
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: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 6
|
10
|
+
version: 0.6.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Cole
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-12-
|
18
|
+
date: 2012-12-07 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description: Library for parsing InnoDB data files in Ruby
|
@@ -31,6 +31,7 @@ files:
|
|
31
31
|
- lib/innodb.rb
|
32
32
|
- lib/innodb/cursor.rb
|
33
33
|
- lib/innodb/free_list.rb
|
34
|
+
- lib/innodb/fseg_entry.rb
|
34
35
|
- lib/innodb/index.rb
|
35
36
|
- lib/innodb/log.rb
|
36
37
|
- lib/innodb/log_block.rb
|
@@ -38,6 +39,7 @@ files:
|
|
38
39
|
- lib/innodb/page/fsp_hdr_xdes.rb
|
39
40
|
- lib/innodb/page/index.rb
|
40
41
|
- lib/innodb/page/inode.rb
|
42
|
+
- lib/innodb/page/trx_sys.rb
|
41
43
|
- lib/innodb/record_describer.rb
|
42
44
|
- lib/innodb/space.rb
|
43
45
|
- lib/innodb/version.rb
|