innodb_ruby 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|