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 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