innodb_ruby 0.7.1 → 0.7.2
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 +4 -5
- data/lib/innodb/fseg_entry.rb +2 -0
- data/lib/innodb/index.rb +13 -1
- data/lib/innodb/list.rb +72 -4
- data/lib/innodb/page.rb +14 -2
- data/lib/innodb/page/fsp_hdr_xdes.rb +5 -0
- data/lib/innodb/page/index.rb +23 -20
- data/lib/innodb/page/inode.rb +32 -9
- data/lib/innodb/page/trx_sys.rb +1 -0
- data/lib/innodb/record_describer.rb +1 -0
- data/lib/innodb/space.rb +12 -0
- data/lib/innodb/version.rb +1 -1
- data/lib/innodb/xdes.rb +90 -6
- metadata +3 -3
data/bin/innodb_space
CHANGED
@@ -141,11 +141,10 @@ def print_xdes_list(list)
|
|
141
141
|
list.each do |entry|
|
142
142
|
puts "%-12i%-64s" % [
|
143
143
|
entry.xdes[:start_page],
|
144
|
-
entry.
|
145
|
-
[
|
146
|
-
|
147
|
-
|
148
|
-
}.flatten.join,
|
144
|
+
entry.each_page_status.inject("") { |bitmap, page_status|
|
145
|
+
bitmap += page_status[:free] ? "." : "#"
|
146
|
+
bitmap
|
147
|
+
},
|
149
148
|
]
|
150
149
|
end
|
151
150
|
end
|
data/lib/innodb/fseg_entry.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# An InnoDB file segment entry, which appears in a few places, such as the
|
2
2
|
# FSEG header of INDEX pages, and in the TRX_SYS pages.
|
3
3
|
class Innodb::FsegEntry
|
4
|
+
# The size (in bytes) of an FSEG entry, which contains a two 32-bit integers
|
5
|
+
# and a 16-bit integer.
|
4
6
|
SIZE = 4 + 4 + 2
|
5
7
|
|
6
8
|
# Return the FSEG entry address, which points to an entry on an INODE page.
|
data/lib/innodb/index.rb
CHANGED
@@ -75,6 +75,10 @@ class Innodb::Index
|
|
75
75
|
|
76
76
|
# Iterate through all pages at this level starting with the provided page.
|
77
77
|
def each_page_from(page)
|
78
|
+
unless block_given?
|
79
|
+
return Enumerable::Enumerator.new(self, :each_page_from, page)
|
80
|
+
end
|
81
|
+
|
78
82
|
while page && page.type == :INDEX
|
79
83
|
yield page
|
80
84
|
page = @space.page(page.next)
|
@@ -84,11 +88,19 @@ class Innodb::Index
|
|
84
88
|
# Iterate through all pages at the given level by finding the first page
|
85
89
|
# and following the next pointers in each page.
|
86
90
|
def each_page_at_level(level)
|
91
|
+
unless block_given?
|
92
|
+
return Enumerable::Enumerator.new(self, :each_page_at_level, level)
|
93
|
+
end
|
94
|
+
|
87
95
|
each_page_from(first_page_at_level(level)) { |page| yield page }
|
88
96
|
end
|
89
97
|
|
90
98
|
# Iterate through all records on all leaf pages in ascending order.
|
91
99
|
def each_record
|
100
|
+
unless block_given?
|
101
|
+
return Enumerable::Enumerator.new(self, :each_record)
|
102
|
+
end
|
103
|
+
|
92
104
|
each_page_at_level(0) do |page|
|
93
105
|
page.each_record do |record|
|
94
106
|
yield record
|
@@ -230,7 +242,7 @@ class Innodb::Index
|
|
230
242
|
def binary_search(key)
|
231
243
|
page = @root
|
232
244
|
|
233
|
-
while rec = binary_search_by_directory(page, page.directory
|
245
|
+
while rec = binary_search_by_directory(page, page.directory, key)
|
234
246
|
if page.level > 0
|
235
247
|
# If we haven't reached a leaf page yet, move down the tree and search
|
236
248
|
# again using binary search.
|
data/lib/innodb/list.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
# An abstract InnoDB "free list" or FLST (renamed to just "list" here as it
|
2
|
+
# frequently is used for structures that aren't free lists). This class must
|
3
|
+
# be sub-classed to provide an appropriate #object_from_address method.
|
1
4
|
class Innodb::List
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
5
|
+
# An "address", which consists of a page number and byte offset within the
|
6
|
+
# page. This points to the list "node" pointers (prev and next) of the
|
7
|
+
# node, not necessarily the node object.
|
8
|
+
ADDRESS_SIZE = 4 + 2
|
9
|
+
|
10
|
+
# Read a node address from a cursor. Return nil if the address is an end
|
11
|
+
# or "NULL" pointer (the page number is UINT32_MAX), or the address if
|
12
|
+
# valid.
|
6
13
|
def self.get_address(cursor)
|
7
14
|
page = Innodb::Page.maybe_undefined(cursor.get_uint32)
|
8
15
|
offset = cursor.get_uint16
|
@@ -14,6 +21,13 @@ class Innodb::List
|
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
24
|
+
# A list node consists of two addresses: the "previous" node address, and
|
25
|
+
# the "next" node address.
|
26
|
+
NODE_SIZE = 2 * ADDRESS_SIZE
|
27
|
+
|
28
|
+
# Read a node, consisting of two consecutive addresses (:prev and :next)
|
29
|
+
# from a cursor. Either address may be nil, indicating the end of the
|
30
|
+
# linked list.
|
17
31
|
def self.get_node(cursor)
|
18
32
|
{
|
19
33
|
:prev => get_address(cursor),
|
@@ -21,6 +35,15 @@ class Innodb::List
|
|
21
35
|
}
|
22
36
|
end
|
23
37
|
|
38
|
+
# A list base node consists of a list length followed by two addresses:
|
39
|
+
# the "first" node address, and the "last" node address.
|
40
|
+
BASE_NODE_SIZE = 4 + (2 * ADDRESS_SIZE)
|
41
|
+
|
42
|
+
# Read a base node, consisting of a list length followed by two addresses
|
43
|
+
# (:first and :last) from a cursor. Either address may be nil. An empty list
|
44
|
+
# has a :length of 0 and :first and :last are nil. A list with only a single
|
45
|
+
# item will have a :length of 1 and :first and :last will point to the same
|
46
|
+
# address.
|
24
47
|
def self.get_base_node(cursor)
|
25
48
|
{
|
26
49
|
:length => cursor.get_uint32,
|
@@ -37,43 +60,80 @@ class Innodb::List
|
|
37
60
|
attr_reader :space
|
38
61
|
attr_reader :base
|
39
62
|
|
63
|
+
# Abstract #object_from_address method which must be implemented by
|
64
|
+
# sub-classes in order to return a useful object given an object address.
|
65
|
+
def object_from_address(address)
|
66
|
+
raise "#{self.class} must implement object_from_address"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the object pointed to by the "previous" address pointer of the
|
70
|
+
# provided object.
|
40
71
|
def prev(object)
|
72
|
+
unless object.respond_to? :prev_address
|
73
|
+
raise "Class #{object.class} does not respond to prev_address"
|
74
|
+
end
|
75
|
+
|
41
76
|
object_from_address(object.prev_address)
|
42
77
|
end
|
43
78
|
|
79
|
+
# Return the object pointed to by the "next" address pointer of the
|
80
|
+
# provided object.
|
44
81
|
def next(object)
|
82
|
+
unless object.respond_to? :next_address
|
83
|
+
raise "Class #{object.class} does not respond to next_address"
|
84
|
+
end
|
85
|
+
|
45
86
|
object_from_address(object.next_address)
|
46
87
|
end
|
47
88
|
|
89
|
+
# Return the first object in the list using the list base node "first"
|
90
|
+
# address pointer.
|
48
91
|
def first
|
49
92
|
object_from_address(@base[:first])
|
50
93
|
end
|
51
94
|
|
95
|
+
# Return the first object in the list using the list base node "last"
|
96
|
+
# address pointer.
|
52
97
|
def last
|
53
98
|
object_from_address(@base[:last])
|
54
99
|
end
|
55
100
|
|
101
|
+
# Return a list cursor for the list.
|
56
102
|
def cursor(node=nil)
|
57
103
|
Cursor.new(self, node)
|
58
104
|
end
|
59
105
|
|
106
|
+
# Iterate through all nodes in the list.
|
60
107
|
def each
|
108
|
+
unless block_given?
|
109
|
+
return Enumerable::Enumerator.new(self)
|
110
|
+
end
|
111
|
+
|
61
112
|
c = cursor
|
62
113
|
while e = c.next
|
63
114
|
yield e
|
64
115
|
end
|
65
116
|
end
|
66
117
|
|
118
|
+
# A list iteration cursor used primarily by the Innodb::List #cursor method
|
119
|
+
# implicitly. Keeps its own state for iterating through lists efficiently.
|
67
120
|
class Cursor
|
68
121
|
def initialize(list, node=nil)
|
69
122
|
@list = list
|
70
123
|
@cursor = node
|
71
124
|
end
|
72
125
|
|
126
|
+
# Reset the list cursor to its default starting state, which will allow
|
127
|
+
# iteration forwards from the first entry (using #next) or backwards
|
128
|
+
# from the last entry (using #prev).
|
73
129
|
def reset
|
74
130
|
@cursor = nil
|
75
131
|
end
|
76
132
|
|
133
|
+
# Return the previous entry from the current position, and advance the
|
134
|
+
# cursor position to the returned entry. If the cursor is currently nil,
|
135
|
+
# return the last entry in the list and adjust the cursor position to
|
136
|
+
# that entry.
|
77
137
|
def prev
|
78
138
|
if @cursor
|
79
139
|
@cursor = @list.prev(@cursor)
|
@@ -82,6 +142,10 @@ class Innodb::List
|
|
82
142
|
end
|
83
143
|
end
|
84
144
|
|
145
|
+
# Return the next entry from the current position, and advance the
|
146
|
+
# cursor position to the returned entry. If the cursor is currently nil,
|
147
|
+
# return the first entry in the list and adjust the cursor position to
|
148
|
+
# that entry.
|
85
149
|
def next
|
86
150
|
if @cursor
|
87
151
|
@cursor = @list.next(@cursor)
|
@@ -92,6 +156,8 @@ class Innodb::List
|
|
92
156
|
end
|
93
157
|
end
|
94
158
|
|
159
|
+
# A list of extent descriptor entries. Objects returned by list methods
|
160
|
+
# will be Innodb::Xdes objects.
|
95
161
|
class Innodb::List::Xdes < Innodb::List
|
96
162
|
def object_from_address(address)
|
97
163
|
if address && page = @space.page(address[:page])
|
@@ -100,6 +166,8 @@ class Innodb::List::Xdes < Innodb::List
|
|
100
166
|
end
|
101
167
|
end
|
102
168
|
|
169
|
+
# A list of Inode pages. Objects returned by list methods will be
|
170
|
+
# Innodb::Page::Inode objects.
|
103
171
|
class Innodb::List::Inode < Innodb::List
|
104
172
|
def object_from_address(address)
|
105
173
|
if address && page = @space.page(address[:page])
|
data/lib/innodb/page.rb
CHANGED
@@ -6,15 +6,25 @@ require "innodb/cursor"
|
|
6
6
|
# A page being handled by Innodb::Page indicates that its type is not currently
|
7
7
|
# handled by any more specialized class.
|
8
8
|
class Innodb::Page
|
9
|
+
# A hash of page types to specialized classes to handle them. Normally
|
10
|
+
# subclasses will register themselves in this list.
|
9
11
|
SPECIALIZED_CLASSES = {}
|
10
12
|
|
11
13
|
# Load a page as a generic page in order to make the "fil" header accessible,
|
12
14
|
# and then attempt to hand off the page to a specialized class to be
|
13
15
|
# re-parsed if possible. If there is no specialized class for this type
|
14
16
|
# of page, return the generic object.
|
17
|
+
#
|
18
|
+
# This could be optimized to reach into the page buffer and efficiently
|
19
|
+
# extract the page type in order to avoid throwing away a generic
|
20
|
+
# Innodb::Page object when parsing every specialized page, but this is
|
21
|
+
# a bit cleaner, and we're not particularly performance sensitive.
|
15
22
|
def self.parse(space, buffer)
|
23
|
+
# Create a page object as a generic page.
|
16
24
|
page = Innodb::Page.new(space, buffer)
|
17
25
|
|
26
|
+
# If there is a specialized class available for this page type, re-create
|
27
|
+
# the page object using that specialized class.
|
18
28
|
if specialized_class = SPECIALIZED_CLASSES[page.type]
|
19
29
|
page = specialized_class.new(space, buffer)
|
20
30
|
end
|
@@ -53,7 +63,7 @@ class Innodb::Page
|
|
53
63
|
|
54
64
|
# Return the size of the "fil" header, in bytes.
|
55
65
|
def size_fil_header
|
56
|
-
|
66
|
+
4 + 4 + 4 + 4 + 8 + 2 + 8 + 4
|
57
67
|
end
|
58
68
|
|
59
69
|
# Return the byte offset of the start of the "fil" trailer, which is at
|
@@ -64,7 +74,7 @@ class Innodb::Page
|
|
64
74
|
|
65
75
|
# Return the size of the "fil" trailer, in bytes.
|
66
76
|
def size_fil_trailer
|
67
|
-
|
77
|
+
4 + 4
|
68
78
|
end
|
69
79
|
|
70
80
|
# InnoDB Page Type constants from include/fil0fil.h.
|
@@ -136,6 +146,8 @@ class Innodb::Page
|
|
136
146
|
fil_header[:lsn]
|
137
147
|
end
|
138
148
|
|
149
|
+
# Implement a custom inspect method to avoid irb printing the contents of
|
150
|
+
# the page buffer, since it's very large and mostly not interesting.
|
139
151
|
def inspect
|
140
152
|
if fil_header
|
141
153
|
"#<%s: size=%i, space_id=%i, offset=%i, type=%s, prev=%i, next=%i>" % [
|
@@ -61,12 +61,17 @@ class Innodb::Page::FspHdrXdes < Innodb::Page
|
|
61
61
|
# field in the XDES entry indicates which type of list it is present in,
|
62
62
|
# although not necessarily which list (e.g. :fseg).
|
63
63
|
def each_xdes
|
64
|
+
unless block_given?
|
65
|
+
return Enumerable::Enumerator.new(self, :each_xdes)
|
66
|
+
end
|
67
|
+
|
64
68
|
c = cursor(pos_xdes_array)
|
65
69
|
XDES_N_ARRAY_ENTRIES.times do
|
66
70
|
yield Innodb::Xdes.new(self, c)
|
67
71
|
end
|
68
72
|
end
|
69
73
|
|
74
|
+
# Dump the contents of a page for debugging purposes.
|
70
75
|
def dump
|
71
76
|
super
|
72
77
|
|
data/lib/innodb/page/index.rb
CHANGED
@@ -116,13 +116,13 @@ class Innodb::Page::Index < Innodb::Page
|
|
116
116
|
data(pos_user_records, page_header[:heap_top] - pos_user_records)
|
117
117
|
end
|
118
118
|
|
119
|
-
# Page direction values possible in the page_header
|
119
|
+
# Page direction values possible in the page_header's :direction field.
|
120
120
|
PAGE_DIRECTION = {
|
121
|
-
1 => :left,
|
122
|
-
2 => :right,
|
123
|
-
3 => :same_rec,
|
124
|
-
4 => :same_page,
|
125
|
-
5 => :no_direction,
|
121
|
+
1 => :left, # Inserts have been in descending order.
|
122
|
+
2 => :right, # Inserts have been in ascending order.
|
123
|
+
3 => :same_rec, # Unused by InnoDB.
|
124
|
+
4 => :same_page, # Unused by InnoDB.
|
125
|
+
5 => :no_direction, # Inserts have been in random order.
|
126
126
|
}
|
127
127
|
|
128
128
|
# Return the "index" header.
|
@@ -172,21 +172,21 @@ class Innodb::Page::Index < Innodb::Page
|
|
172
172
|
end
|
173
173
|
|
174
174
|
# The size (in bytes) of the bit-packed fields in each record header.
|
175
|
-
RECORD_BITS_SIZE
|
175
|
+
RECORD_BITS_SIZE = 3
|
176
176
|
|
177
177
|
# The size (in bytes) of the "next" pointer in each record header.
|
178
|
-
RECORD_NEXT_SIZE
|
178
|
+
RECORD_NEXT_SIZE = 2
|
179
179
|
|
180
180
|
# The size (in bytes) of the record pointers in each page directory slot.
|
181
|
-
PAGE_DIR_SLOT_SIZE
|
181
|
+
PAGE_DIR_SLOT_SIZE = 2
|
182
182
|
|
183
183
|
# The minimum number of records "owned" by each record with an entry in
|
184
184
|
# the page directory.
|
185
|
-
PAGE_DIR_SLOT_MIN_N_OWNED
|
185
|
+
PAGE_DIR_SLOT_MIN_N_OWNED = 4
|
186
186
|
|
187
187
|
# The maximum number of records "owned" by each record with an entry in
|
188
188
|
# the page directory.
|
189
|
-
PAGE_DIR_SLOT_MAX_N_OWNED
|
189
|
+
PAGE_DIR_SLOT_MAX_N_OWNED = 8
|
190
190
|
|
191
191
|
# Return the size of the header for each record.
|
192
192
|
def size_record_header
|
@@ -211,10 +211,10 @@ class Innodb::Page::Index < Innodb::Page
|
|
211
211
|
|
212
212
|
# Record types used in the :type field of the record header.
|
213
213
|
RECORD_TYPES = {
|
214
|
-
0 => :conventional,
|
215
|
-
1 => :node_pointer,
|
216
|
-
2 => :infimum,
|
217
|
-
3 => :supremum,
|
214
|
+
0 => :conventional, # A normal user record in a leaf page.
|
215
|
+
1 => :node_pointer, # A node pointer in a non-leaf page.
|
216
|
+
2 => :infimum, # The system "infimum" record.
|
217
|
+
3 => :supremum, # The system "supremum" record.
|
218
218
|
}
|
219
219
|
|
220
220
|
# This record is the minimum record at this level of the B-tree.
|
@@ -225,8 +225,6 @@ class Innodb::Page::Index < Innodb::Page
|
|
225
225
|
|
226
226
|
# Return the header from a record.
|
227
227
|
def record_header(offset)
|
228
|
-
return nil unless type == :INDEX
|
229
|
-
|
230
228
|
c = cursor(offset).backward
|
231
229
|
case page_header[:format]
|
232
230
|
when :compact
|
@@ -307,8 +305,6 @@ class Innodb::Page::Index < Innodb::Page
|
|
307
305
|
# Parse and return simple fixed-format system records, such as InnoDB's
|
308
306
|
# internal infimum and supremum records.
|
309
307
|
def system_record(offset)
|
310
|
-
return nil unless type == :INDEX
|
311
|
-
|
312
308
|
header = record_header(offset)
|
313
309
|
{
|
314
310
|
:offset => offset,
|
@@ -356,7 +352,6 @@ class Innodb::Page::Index < Innodb::Page
|
|
356
352
|
# Parse and return a record at a given offset.
|
357
353
|
def record(offset)
|
358
354
|
return nil unless offset
|
359
|
-
return nil unless type == :INDEX
|
360
355
|
return infimum if offset == pos_infimum
|
361
356
|
return supremum if offset == pos_supremum
|
362
357
|
|
@@ -446,6 +441,10 @@ class Innodb::Page::Index < Innodb::Page
|
|
446
441
|
|
447
442
|
# Iterate through all records.
|
448
443
|
def each_record
|
444
|
+
unless block_given?
|
445
|
+
return Enumerable::Enumerator.new(self, :each_record)
|
446
|
+
end
|
447
|
+
|
449
448
|
c = record_cursor(infimum[:next])
|
450
449
|
|
451
450
|
while rec = c.record
|
@@ -461,6 +460,10 @@ class Innodb::Page::Index < Innodb::Page
|
|
461
460
|
def each_child_page
|
462
461
|
return nil if level == 0
|
463
462
|
|
463
|
+
unless block_given?
|
464
|
+
return Enumerable::Enumerator.new(self, :each_child_page)
|
465
|
+
end
|
466
|
+
|
464
467
|
each_record do |rec|
|
465
468
|
yield rec[:child_page_number], rec[:key]
|
466
469
|
end
|
data/lib/innodb/page/inode.rb
CHANGED
@@ -15,40 +15,59 @@ class Innodb::Page::Inode < Innodb::Page
|
|
15
15
|
# and populated with valid data.
|
16
16
|
MAGIC_N_VALUE = 97937874
|
17
17
|
|
18
|
-
|
18
|
+
# Return the byte offset of the list node, which immediately follows the
|
19
|
+
# FIL header.
|
20
|
+
def pos_list_entry
|
19
21
|
pos_fil_header + size_fil_header
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
+
# Return the byte offset of the list node.
|
25
|
+
def size_list_entry
|
26
|
+
Innodb::List::NODE_SIZE
|
24
27
|
end
|
25
28
|
|
29
|
+
# Return the byte offset of the Inode array in the page, which immediately
|
30
|
+
# follows the list entry.
|
31
|
+
def pos_inode_array
|
32
|
+
pos_list_entry + size_list_entry
|
33
|
+
end
|
34
|
+
|
35
|
+
# The size (in bytes) of an Inode entry.
|
26
36
|
def size_inode
|
27
37
|
(16 + (3 * Innodb::List::BASE_NODE_SIZE) +
|
28
38
|
(FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
|
29
39
|
end
|
30
40
|
|
41
|
+
# The number of Inode entries that fit on a page.
|
31
42
|
def inodes_per_page
|
32
|
-
(size -
|
33
|
-
end
|
34
|
-
|
35
|
-
def page_number_array(size, cursor)
|
36
|
-
size.times.map { |n| Innodb::Page.maybe_undefined(cursor.get_uint32) }
|
43
|
+
(size - pos_inode_array - 10) / size_inode
|
37
44
|
end
|
38
45
|
|
46
|
+
# Return the list entry.
|
39
47
|
def list_entry
|
40
48
|
c = cursor(pos_inode_list_entry)
|
41
49
|
Innodb::List.get_node(c)
|
42
50
|
end
|
43
51
|
|
52
|
+
# Return the "previous" address pointer from the list entry. This is used
|
53
|
+
# by Innodb::List::Inode to iterate through Inode lists.
|
44
54
|
def prev_address
|
45
55
|
list_entry[:prev]
|
46
56
|
end
|
47
57
|
|
58
|
+
# Return the "next" address pointer from the list entry. This is used
|
59
|
+
# by Innodb::List::Inode to iterate through Inode lists.
|
48
60
|
def next_address
|
49
61
|
list_entry[:next]
|
50
62
|
end
|
51
63
|
|
64
|
+
# Read an array of page numbers (32-bit integers, which may be nil) from
|
65
|
+
# the provided cursor.
|
66
|
+
def page_number_array(size, cursor)
|
67
|
+
size.times.map { |n| Innodb::Page.maybe_undefined(cursor.get_uint32) }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Read a single Inode entry from the provided cursor.
|
52
71
|
def inode(cursor)
|
53
72
|
{
|
54
73
|
:fseg_id => cursor.get_uint64,
|
@@ -64,18 +83,22 @@ class Innodb::Page::Inode < Innodb::Page
|
|
64
83
|
}
|
65
84
|
end
|
66
85
|
|
86
|
+
# Read a single Inode entry from the provided byte offset by creating a
|
87
|
+
# cursor and reading the inode using the inode method.
|
67
88
|
def inode_at(offset)
|
68
89
|
inode(cursor(offset))
|
69
90
|
end
|
70
91
|
|
92
|
+
# Iterate through all Inodes in the inode array.
|
71
93
|
def each_inode
|
72
|
-
inode_cursor = cursor(
|
94
|
+
inode_cursor = cursor(pos_inode_array)
|
73
95
|
inodes_per_page.times do
|
74
96
|
this_inode = inode(inode_cursor)
|
75
97
|
yield this_inode if this_inode[:fseg_id] != 0
|
76
98
|
end
|
77
99
|
end
|
78
100
|
|
101
|
+
# Dump the contents of a page for debugging purposes.
|
79
102
|
def dump
|
80
103
|
super
|
81
104
|
|
data/lib/innodb/page/trx_sys.rb
CHANGED
data/lib/innodb/space.rb
CHANGED
@@ -46,6 +46,10 @@ class Innodb::Space
|
|
46
46
|
# root page. This should work fine for IBD files, but not for ibdata
|
47
47
|
# files.
|
48
48
|
def each_index
|
49
|
+
unless block_given?
|
50
|
+
return Enumerable::Enumerator.new(self, :each_index)
|
51
|
+
end
|
52
|
+
|
49
53
|
(3...@pages).each do |page_number|
|
50
54
|
if page(page_number).root?
|
51
55
|
yield index(page_number)
|
@@ -58,6 +62,10 @@ class Innodb::Space
|
|
58
62
|
# Iterate through all pages in a tablespace, returning the page number
|
59
63
|
# and an Innodb::Page object for each one.
|
60
64
|
def each_page
|
65
|
+
unless block_given?
|
66
|
+
return Enumerable::Enumerator.new(self, :each_page)
|
67
|
+
end
|
68
|
+
|
61
69
|
(0...@pages).each do |page_number|
|
62
70
|
current_page = page(page_number)
|
63
71
|
yield page_number, current_page if current_page
|
@@ -67,6 +75,10 @@ class Innodb::Space
|
|
67
75
|
# Iterate through unique regions in the space by page type. This is useful
|
68
76
|
# to achieve an overall view of the space.
|
69
77
|
def each_page_type_region
|
78
|
+
unless block_given?
|
79
|
+
return Enumerable::Enumerator.new(self, :each_page_type_region)
|
80
|
+
end
|
81
|
+
|
70
82
|
region = nil
|
71
83
|
each_page do |page_number, page|
|
72
84
|
if region && region[:type] == page.type
|
data/lib/innodb/version.rb
CHANGED
data/lib/innodb/xdes.rb
CHANGED
@@ -1,14 +1,52 @@
|
|
1
|
+
# An InnoDB "extent descriptor entry" or "+XDES+". These structures are used
|
2
|
+
# in the +XDES+ entry array contained in +FSP_HDR+ and +XDES+ pages.
|
3
|
+
#
|
4
|
+
# Note the distinction between +XDES+ _entries_ and +XDES+ _pages_.
|
1
5
|
class Innodb::Xdes
|
6
|
+
# Number of pages contained in an extent. InnoDB extents are normally
|
7
|
+
# 64 pages, or 1MiB in size.
|
2
8
|
PAGES_PER_EXTENT = 64
|
9
|
+
|
10
|
+
# Number of bits per page in the +XDES+ entry bitmap field. Currently
|
11
|
+
# +XDES+ entries store two bits per page, with the following meanings:
|
12
|
+
#
|
13
|
+
# * 1 = free (the page is free, or not in use)
|
14
|
+
# * 2 = clean (currently unused, always 1 when initialized)
|
3
15
|
BITS_PER_PAGE = 2
|
16
|
+
|
17
|
+
# The bit value for a free page.
|
18
|
+
BITMAP_BV_FREE = 1
|
19
|
+
|
20
|
+
# The bit value for a clean page (currently unused in InnoDB).
|
21
|
+
BITMAP_BV_CLEAN = 2
|
22
|
+
|
23
|
+
# The bitwise-OR of all bitmap bit values.
|
24
|
+
BITMAP_BV_ALL = (BITMAP_BV_FREE | BITMAP_BV_CLEAN)
|
25
|
+
|
26
|
+
# Size (in bytes) of the bitmap field in the +XDES+ entry.
|
4
27
|
BITMAP_SIZE = (PAGES_PER_EXTENT * BITS_PER_PAGE) / 8
|
28
|
+
|
29
|
+
# Size (in bytes) of the an +XDES+ entry.
|
5
30
|
ENTRY_SIZE = 8 + Innodb::List::NODE_SIZE + 4 + BITMAP_SIZE
|
6
31
|
|
32
|
+
# The values used in the +:state+ field indicating what the extent is
|
33
|
+
# used for (or what list it is on).
|
7
34
|
STATES = {
|
8
|
-
1 => :free,
|
9
|
-
|
10
|
-
|
11
|
-
|
35
|
+
1 => :free, # The extent is completely empty and unused, and should
|
36
|
+
# be present on the filespace's FREE list.
|
37
|
+
|
38
|
+
2 => :free_frag, # Some pages of the extent are used individually, and
|
39
|
+
# the extent should be present on the filespace's
|
40
|
+
# FREE_FRAG list.
|
41
|
+
|
42
|
+
3 => :full_frag, # All pages of the extent are used individually, and
|
43
|
+
# the extent should be present on the filespace's
|
44
|
+
# FULL_FRAG list.
|
45
|
+
|
46
|
+
4 => :fseg, # The extent is wholly allocated to a file segment.
|
47
|
+
# Additional information about the state of this extent
|
48
|
+
# can be derived from the its presence on particular
|
49
|
+
# file segment lists (FULL, NOT_FULL, or FREE).
|
12
50
|
}
|
13
51
|
|
14
52
|
def initialize(page, cursor)
|
@@ -25,15 +63,61 @@ class Innodb::Xdes
|
|
25
63
|
}
|
26
64
|
end
|
27
65
|
|
66
|
+
# Return the stored extent descriptor entry.
|
28
67
|
def xdes
|
29
68
|
@xdes
|
30
69
|
end
|
31
70
|
|
71
|
+
# Iterate through all pages represented by this extent descriptor,
|
72
|
+
# yielding a page status hash for each page, containing the following
|
73
|
+
# fields:
|
74
|
+
#
|
75
|
+
# :page The page number.
|
76
|
+
# :free Boolean indicating whether the page is free.
|
77
|
+
# :clean Boolean indicating whether the page is clean (currently
|
78
|
+
# this bit is unused by InnoDB, and always set true).
|
79
|
+
def each_page_status
|
80
|
+
unless block_given?
|
81
|
+
return Enumerable::Enumerator.new(self, :each_page_status)
|
82
|
+
end
|
83
|
+
|
84
|
+
xdes[:bitmap].bytes.each_with_index do |byte, byte_index|
|
85
|
+
(0..3).each_with_index do |page, page_index|
|
86
|
+
page_number = xdes[:start_page] + (byte_index * 4) + page_index
|
87
|
+
page_bits = ((byte >> (page * BITS_PER_PAGE)) & BITMAP_BV_ALL)
|
88
|
+
page_status = {
|
89
|
+
:page => page_number,
|
90
|
+
:free => (page_bits & BITMAP_BV_FREE != 0),
|
91
|
+
:clean => (page_bits & BITMAP_BV_CLEAN != 0),
|
92
|
+
}
|
93
|
+
yield page_status
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Return the count of free pages (free bit is true) on this extent.
|
101
|
+
def free_pages
|
102
|
+
each_page_status.inject(0) { |sum, p| sum += 1 if p[:free]; sum }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Return the count of used pages (free bit is false) on this extent.
|
106
|
+
def used_pages
|
107
|
+
PAGES_PER_EXTENT - free_pages
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return the address of the previous list pointer from the list node
|
111
|
+
# contained within the XDES entry. This is used by +Innodb::List::Xdes+
|
112
|
+
# to iterate through XDES entries in a list.
|
32
113
|
def prev_address
|
33
|
-
|
114
|
+
xdes[:list][:prev]
|
34
115
|
end
|
35
116
|
|
117
|
+
# Return the address of the next list pointer from the list node
|
118
|
+
# contained within the XDES entry. This is used by +Innodb::List::Xdes+
|
119
|
+
# to iterate through XDES entries in a list.
|
36
120
|
def next_address
|
37
|
-
|
121
|
+
xdes[:list][:next]
|
38
122
|
end
|
39
123
|
end
|
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
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 2
|
10
|
+
version: 0.7.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jeremy Cole
|