innodb_ruby 0.6.4 → 0.6.6
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_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
|