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 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.xdes[:bitmap].bytes.map { |byte|
145
- [0, 2, 4, 6].map { |shift|
146
- ((byte >> shift) & 1 == 1) ? "." : "#"
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
@@ -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.dup, key)
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
- FIL_ADDR_SIZE = 4 + 2
3
- NODE_SIZE = 2 * FIL_ADDR_SIZE
4
- BASE_NODE_SIZE = 4 + (2 * FIL_ADDR_SIZE)
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
- 38
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
- 8
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
 
@@ -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[:direction] field.
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 = 3
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 = 2
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 = 2
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 = 4
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 = 8
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
@@ -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
- def pos_inode_list_entry
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
- def pos_inode_list
23
- pos_fil_header + size_fil_header + Innodb::List::NODE_SIZE
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 - pos_inode_list - 10) / size_inode
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(pos_inode_list)
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
 
@@ -90,6 +90,7 @@ class Innodb::Page::TrxSys < Innodb::Page
90
90
  }
91
91
  end
92
92
 
93
+ # Dump the contents of a page for debugging purposes.
93
94
  def dump
94
95
  super
95
96
 
@@ -1 +1,2 @@
1
+ # An empty class in order to establish the Innodb::RecordDescriber namespace.
1
2
  class Innodb::RecordDescriber; end
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
@@ -1,3 +1,3 @@
1
1
  module Innodb
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.2"
3
3
  end
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
- 2 => :free_frag,
10
- 3 => :full_frag,
11
- 4 => :fseg,
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
- @xdes[:list][:prev]
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
- @xdes[:list][:next]
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: 1
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 7
9
- - 1
10
- version: 0.7.1
9
+ - 2
10
+ version: 0.7.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeremy Cole