innodb_ruby 0.9.14 → 0.12.0

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +654 -778
  5. data/lib/innodb/checksum.rb +26 -24
  6. data/lib/innodb/data_dictionary.rb +490 -550
  7. data/lib/innodb/data_type.rb +362 -325
  8. data/lib/innodb/field.rb +102 -89
  9. data/lib/innodb/fseg_entry.rb +22 -26
  10. data/lib/innodb/history.rb +21 -21
  11. data/lib/innodb/history_list.rb +72 -76
  12. data/lib/innodb/ibuf_bitmap.rb +36 -36
  13. data/lib/innodb/ibuf_index.rb +6 -2
  14. data/lib/innodb/index.rb +245 -276
  15. data/lib/innodb/inode.rb +166 -124
  16. data/lib/innodb/list.rb +196 -183
  17. data/lib/innodb/log.rb +139 -110
  18. data/lib/innodb/log_block.rb +100 -91
  19. data/lib/innodb/log_group.rb +53 -64
  20. data/lib/innodb/log_reader.rb +97 -96
  21. data/lib/innodb/log_record.rb +328 -279
  22. data/lib/innodb/lsn.rb +86 -81
  23. data/lib/innodb/page/blob.rb +82 -83
  24. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  25. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  26. data/lib/innodb/page/index.rb +965 -924
  27. data/lib/innodb/page/index_compressed.rb +34 -34
  28. data/lib/innodb/page/inode.rb +103 -112
  29. data/lib/innodb/page/sys.rb +13 -15
  30. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  31. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  32. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  33. data/lib/innodb/page/trx_sys.rb +204 -182
  34. data/lib/innodb/page/undo_log.rb +106 -92
  35. data/lib/innodb/page.rb +417 -414
  36. data/lib/innodb/record.rb +121 -164
  37. data/lib/innodb/record_describer.rb +66 -68
  38. data/lib/innodb/space.rb +381 -413
  39. data/lib/innodb/stats.rb +33 -35
  40. data/lib/innodb/system.rb +149 -171
  41. data/lib/innodb/undo_log.rb +129 -107
  42. data/lib/innodb/undo_record.rb +255 -247
  43. data/lib/innodb/util/buffer_cursor.rb +81 -79
  44. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  45. data/lib/innodb/version.rb +2 -2
  46. data/lib/innodb/xdes.rb +144 -142
  47. data/lib/innodb.rb +4 -5
  48. metadata +100 -25
data/lib/innodb/inode.rb CHANGED
@@ -1,144 +1,186 @@
1
- # -*- encoding : utf-8 -*-
2
-
3
- class Innodb::Inode
4
- # The number of "slots" (each representing one page) in the fragment array
5
- # within each Inode entry.
6
- FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
7
-
8
- # The size (in bytes) of each slot in the fragment array.
9
- FRAG_SLOT_SIZE = 4
10
-
11
- # A magic number which helps determine if an Inode structure is in use
12
- # and populated with valid data.
13
- MAGIC_N_VALUE = 97937874
14
-
15
- # The size (in bytes) of an Inode entry.
16
- SIZE = (16 + (3 * Innodb::List::BASE_NODE_SIZE) +
17
- (FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
18
-
19
- # Read an array of page numbers (32-bit integers, which may be nil) from
20
- # the provided cursor.
21
- def self.page_number_array(size, cursor)
22
- size.times.map do |n|
23
- cursor.name("page[#{n}]") do |c|
24
- Innodb::Page.maybe_undefined(c.get_uint32)
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module Innodb
6
+ class Inode
7
+ extend Forwardable
8
+
9
+ Header = Struct.new(
10
+ :offset,
11
+ :fseg_id,
12
+ :not_full_n_used,
13
+ :free,
14
+ :not_full,
15
+ :full,
16
+ :magic_n,
17
+ :frag_array,
18
+ keyword_init: true
19
+ )
20
+
21
+ # The number of "slots" (each representing one page) in the fragment array
22
+ # within each Inode entry.
23
+ FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
24
+
25
+ # The size (in bytes) of each slot in the fragment array.
26
+ FRAG_SLOT_SIZE = 4
27
+
28
+ # A magic number which helps determine if an Inode structure is in use
29
+ # and populated with valid data.
30
+ MAGIC_N_VALUE = 97_937_874
31
+
32
+ # The size (in bytes) of an Inode entry.
33
+ SIZE = (16 + (3 * Innodb::List::BASE_NODE_SIZE) +
34
+ (FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
35
+
36
+ LISTS = %i[
37
+ free
38
+ not_full
39
+ full
40
+ ].freeze
41
+
42
+ # Read an array of page numbers (32-bit integers, which may be nil) from
43
+ # the provided cursor.
44
+ def self.page_number_array(size, cursor)
45
+ size.times.map do |n|
46
+ cursor.name("page[#{n}]") do |c|
47
+ Innodb::Page.maybe_undefined(c.read_uint32)
48
+ end
25
49
  end
26
50
  end
27
- end
28
51
 
29
- # Construct a new Inode by reading an FSEG header from a cursor.
30
- def self.new_from_cursor(space, cursor)
31
- data = {
32
- :offset => cursor.position,
33
- :fseg_id => cursor.name("fseg_id") {
34
- cursor.get_uint64
35
- },
36
- :not_full_n_used => cursor.name("not_full_n_used") {
37
- cursor.get_uint32
38
- },
39
- :free => cursor.name("list[free]") {
40
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
41
- },
42
- :not_full => cursor.name("list[not_full]") {
43
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
44
- },
45
- :full => cursor.name("list[full]") {
46
- Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor))
47
- },
48
- :magic_n => cursor.name("magic_n") {
49
- cursor.get_uint32
50
- },
51
- :frag_array => cursor.name("frag_array") {
52
- page_number_array(FRAG_ARRAY_N_SLOTS, cursor)
53
- },
54
- }
55
-
56
- Innodb::Inode.new(space, data)
57
- end
52
+ # Construct a new Inode by reading an FSEG header from a cursor.
53
+ def self.new_from_cursor(space, cursor)
54
+ Innodb::Inode.new(
55
+ space,
56
+ Header.new(
57
+ offset: cursor.position,
58
+ fseg_id: cursor.name("fseg_id") { cursor.read_uint64 },
59
+ not_full_n_used: cursor.name("not_full_n_used") { cursor.read_uint32 },
60
+ free: cursor.name("list[free]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
61
+ not_full: cursor.name("list[not_full]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
62
+ full: cursor.name("list[full]") { Innodb::List::Xdes.new(space, Innodb::List.get_base_node(cursor)) },
63
+ magic_n: cursor.name("magic_n") { cursor.read_uint32 },
64
+ frag_array: cursor.name("frag_array") { page_number_array(FRAG_ARRAY_N_SLOTS, cursor) }
65
+ )
66
+ )
67
+ end
58
68
 
59
- attr_accessor :space
69
+ attr_accessor :space
70
+ attr_accessor :header
60
71
 
61
- def initialize(space, data)
62
- @space = space
63
- @data = data
64
- end
72
+ def initialize(space, header)
73
+ @space = space
74
+ @header = header
75
+ end
65
76
 
66
- def offset; @data[:offset]; end
67
- def fseg_id; @data[:fseg_id]; end
68
- def not_full_n_used; @data[:not_full_n_used]; end
69
- def free; @data[:free]; end
70
- def not_full; @data[:not_full]; end
71
- def full; @data[:full]; end
72
- def magic_n; @data[:magic_n]; end
73
- def frag_array; @data[:frag_array]; end
74
-
75
- def inspect
76
- "<%s space=%s, fseg=%i>" % [
77
- self.class.name,
78
- space.inspect,
79
- fseg_id,
80
- ]
81
- end
77
+ def_delegator :header, :offset
78
+ def_delegator :header, :fseg_id
79
+ def_delegator :header, :not_full_n_used
80
+ def_delegator :header, :free
81
+ def_delegator :header, :not_full
82
+ def_delegator :header, :full
83
+ def_delegator :header, :magic_n
84
+ def_delegator :header, :frag_array
85
+
86
+ def inspect
87
+ "<%s space=%s, fseg=%i>" % [
88
+ self.class.name,
89
+ space.inspect,
90
+ fseg_id,
91
+ ]
92
+ end
82
93
 
83
- # Helper method to determine if an Inode is in use. Inodes that are not in
84
- # use have an fseg_id of 0.
85
- def allocated?
86
- fseg_id != 0
87
- end
94
+ # Helper method to determine if an Inode is in use. Inodes that are not in
95
+ # use have an fseg_id of 0.
96
+ def allocated?
97
+ fseg_id != 0
98
+ end
88
99
 
89
- # Helper method to return an array of only non-nil fragment pages.
90
- def frag_array_pages
91
- frag_array.select { |n| ! n.nil? }
92
- end
100
+ # Helper method to return an array of only non-nil fragment pages.
101
+ def frag_array_pages
102
+ frag_array.reject(&:nil?)
103
+ end
93
104
 
94
- # Helper method to count non-nil fragment pages.
95
- def frag_array_n_used
96
- frag_array.inject(0) { |n, i| n += 1 if i; n }
97
- end
105
+ # Helper method to count non-nil fragment pages.
106
+ def frag_array_n_used
107
+ frag_array_pages.count
108
+ end
98
109
 
99
- # Calculate the total number of pages in use (not free) within this fseg.
100
- def used_pages
101
- frag_array_n_used + not_full_n_used +
102
- (full.length * @space.pages_per_extent)
103
- end
110
+ # Calculate the total number of pages in use (not free) within this fseg.
111
+ def used_pages
112
+ frag_array_n_used + not_full_n_used +
113
+ (full.length * @space.pages_per_extent)
114
+ end
104
115
 
105
- # Calculate the total number of pages within this fseg.
106
- def total_pages
107
- frag_array_n_used +
108
- (free.length * @space.pages_per_extent) +
109
- (not_full.length * @space.pages_per_extent) +
110
- (full.length * @space.pages_per_extent)
111
- end
116
+ # Calculate the total number of pages within this fseg.
117
+ def total_pages
118
+ frag_array_n_used +
119
+ (free.length * @space.pages_per_extent) +
120
+ (not_full.length * @space.pages_per_extent) +
121
+ (full.length * @space.pages_per_extent)
122
+ end
112
123
 
113
- # Calculate the fill factor of this fseg, in percent.
114
- def fill_factor
115
- total_pages > 0 ? 100.0 * (used_pages.to_f / total_pages.to_f) : 0.0
116
- end
124
+ # Calculate the fill factor of this fseg, in percent.
125
+ def fill_factor
126
+ total_pages.positive? ? 100.0 * (used_pages.to_f / total_pages) : 0.0
127
+ end
117
128
 
118
- # Return an array of lists within an fseg.
119
- def lists
120
- [:free, :not_full, :full]
121
- end
129
+ # Return a list from the fseg, given its name as a symbol.
130
+ def list(name)
131
+ return unless LISTS.include?(name)
122
132
 
123
- # Return a list from the fseg, given its name as a symbol.
124
- def list(name)
125
- @data[name] if lists.include? name
126
- end
133
+ header[name]
134
+ end
135
+
136
+ # Iterate through all lists, yielding the list name and the list itself.
137
+ def each_list
138
+ return enum_for(:each_list) unless block_given?
139
+
140
+ LISTS.each do |name|
141
+ yield name, list(name)
142
+ end
127
143
 
128
- # Iterate through all lists, yielding the list name and the list itself.
129
- def each_list
130
- lists.each do |name|
131
- yield name, list(name)
144
+ nil
132
145
  end
133
- end
134
146
 
135
- # Compare one Innodb::Inode to another.
136
- def ==(other)
137
- fseg_id == other.fseg_id if other
138
- end
147
+ # Iterate through the fragment array followed by all lists, yielding the
148
+ # page number. This allows a convenient way to identify all pages that are
149
+ # part of this inode.
150
+ def each_page_number(&block)
151
+ return enum_for(:each_page_number) unless block_given?
152
+
153
+ frag_array_pages.each(&block)
154
+
155
+ each_list do |_fseg_name, fseg_list|
156
+ fseg_list.each do |xdes|
157
+ xdes.each_page_status(&block)
158
+ end
159
+ end
139
160
 
140
- # Dump a summary of this object for debugging purposes.
141
- def dump
142
- pp @data
161
+ nil
162
+ end
163
+
164
+ # Iterate through the page as associated with this inode using the
165
+ # each_page_number method, and yield the page number and page.
166
+ def each_page
167
+ return enum_for(:each_page) unless block_given?
168
+
169
+ each_page_number do |page_number|
170
+ yield page_number, space.page(page_number)
171
+ end
172
+
173
+ nil
174
+ end
175
+
176
+ # Compare one Innodb::Inode to another.
177
+ def ==(other)
178
+ fseg_id == other.fseg_id if other
179
+ end
180
+
181
+ # Dump a summary of this object for debugging purposes.
182
+ def dump
183
+ pp header
184
+ end
143
185
  end
144
186
  end
data/lib/innodb/list.rb CHANGED
@@ -1,233 +1,246 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # An abstract InnoDB "free list" or FLST (renamed to just "list" here as it
4
4
  # frequently is used for structures that aren't free lists). This class must
5
5
  # be sub-classed to provide an appropriate #object_from_address method.
6
6
 
7
- class Innodb::List
8
- # An "address", which consists of a page number and byte offset within the
9
- # page. This points to the list "node" pointers (prev and next) of the
10
- # node, not necessarily the node object.
11
- ADDRESS_SIZE = 4 + 2
12
-
13
- # Read a node address from a cursor. Return nil if the address is an end
14
- # or "NULL" pointer (the page number is UINT32_MAX), or the address if
15
- # valid.
16
- def self.get_address(cursor)
17
- page = cursor.name("page") {
18
- Innodb::Page.maybe_undefined(cursor.get_uint32)
19
- }
20
- offset = cursor.name("offset") { cursor.get_uint16 }
21
- if page
22
- {
23
- :page => page,
24
- :offset => offset,
25
- }
7
+ module Innodb
8
+ class List
9
+ BaseNode = Struct.new(
10
+ :length, # rubocop:disable Lint/StructNewOverride
11
+ :first, # rubocop:disable Lint/StructNewOverride
12
+ :last,
13
+ keyword_init: true
14
+ )
15
+
16
+ Node = Struct.new(
17
+ :prev,
18
+ :next,
19
+ keyword_init: true
20
+ )
21
+
22
+ # An "address", which consists of a page number and byte offset within the
23
+ # page. This points to the list "node" pointers (prev and next) of the
24
+ # node, not necessarily the node object.
25
+ ADDRESS_SIZE = 4 + 2
26
+
27
+ # Read a node address from a cursor. Return nil if the address is an end
28
+ # or "NULL" pointer (the page number is UINT32_MAX), or the address if
29
+ # valid.
30
+ def self.get_address(cursor)
31
+ page = cursor.name("page") { Innodb::Page.maybe_undefined(cursor.read_uint32) }
32
+ offset = cursor.name("offset") { cursor.read_uint16 }
33
+
34
+ Innodb::Page::Address.new(page: page, offset: offset) if page
26
35
  end
27
- end
28
-
29
- # A list node consists of two addresses: the "previous" node address, and
30
- # the "next" node address.
31
- NODE_SIZE = 2 * ADDRESS_SIZE
32
-
33
- # Read a node, consisting of two consecutive addresses (:prev and :next)
34
- # from a cursor. Either address may be nil, indicating the end of the
35
- # linked list.
36
- def self.get_node(cursor)
37
- {
38
- :prev => cursor.name("prev") { get_address(cursor) },
39
- :next => cursor.name("next") { get_address(cursor) },
40
- }
41
- end
42
36
 
43
- # A list base node consists of a list length followed by two addresses:
44
- # the "first" node address, and the "last" node address.
45
- BASE_NODE_SIZE = 4 + (2 * ADDRESS_SIZE)
46
-
47
- # Read a base node, consisting of a list length followed by two addresses
48
- # (:first and :last) from a cursor. Either address may be nil. An empty list
49
- # has a :length of 0 and :first and :last are nil. A list with only a single
50
- # item will have a :length of 1 and :first and :last will point to the same
51
- # address.
52
- def self.get_base_node(cursor)
53
- {
54
- :length => cursor.name("length") { cursor.get_uint32 },
55
- :first => cursor.name("first") { get_address(cursor) },
56
- :last => cursor.name("last") { get_address(cursor) },
57
- }
58
- end
37
+ # A list node consists of two addresses: the "previous" node address, and
38
+ # the "next" node address.
39
+ NODE_SIZE = 2 * ADDRESS_SIZE
40
+
41
+ # Read a node, consisting of two consecutive addresses (:prev and :next)
42
+ # from a cursor. Either address may be nil, indicating the end of the
43
+ # linked list.
44
+ def self.get_node(cursor)
45
+ Node.new(
46
+ prev: cursor.name("prev") { get_address(cursor) },
47
+ next: cursor.name("next") { get_address(cursor) }
48
+ )
49
+ end
59
50
 
60
- def initialize(space, base)
61
- @space = space
62
- @base = base
63
- end
51
+ # A list base node consists of a list length followed by two addresses:
52
+ # the "first" node address, and the "last" node address.
53
+ BASE_NODE_SIZE = 4 + (2 * ADDRESS_SIZE)
54
+
55
+ # Read a base node, consisting of a list length followed by two addresses
56
+ # (:first and :last) from a cursor. Either address may be nil. An empty list
57
+ # has a :length of 0 and :first and :last are nil. A list with only a single
58
+ # item will have a :length of 1 and :first and :last will point to the same
59
+ # address.
60
+ def self.get_base_node(cursor)
61
+ BaseNode.new(
62
+ length: cursor.name("length") { cursor.read_uint32 },
63
+ first: cursor.name("first") { get_address(cursor) },
64
+ last: cursor.name("last") { get_address(cursor) }
65
+ )
66
+ end
64
67
 
65
- attr_reader :space
66
- attr_reader :base
68
+ attr_reader :space
69
+ attr_reader :base
67
70
 
68
- # Abstract #object_from_address method which must be implemented by
69
- # sub-classes in order to return a useful object given an object address.
70
- def object_from_address(address)
71
- raise "#{self.class} must implement object_from_address"
72
- end
71
+ def initialize(space, base)
72
+ @space = space
73
+ @base = base
74
+ end
73
75
 
74
- # Return the object pointed to by the "previous" address pointer of the
75
- # provided object.
76
- def prev(object)
77
- unless object.respond_to? :prev_address
78
- raise "Class #{object.class} does not respond to prev_address"
76
+ # Abstract #object_from_address method which must be implemented by
77
+ # sub-classes in order to return a useful object given an object address.
78
+ def object_from_address(_address)
79
+ raise "#{self.class} must implement object_from_address"
79
80
  end
80
81
 
81
- object_from_address(object.prev_address)
82
- end
82
+ # Return the object pointed to by the "previous" address pointer of the
83
+ # provided object.
84
+ def prev(object)
85
+ raise "Class #{object.class} does not respond to prev_address" unless object.respond_to?(:prev_address)
83
86
 
84
- # Return the object pointed to by the "next" address pointer of the
85
- # provided object.
86
- def next(object)
87
- unless object.respond_to? :next_address
88
- raise "Class #{object.class} does not respond to next_address"
87
+ object_from_address(object.prev_address)
89
88
  end
90
89
 
91
- object_from_address(object.next_address)
92
- end
90
+ # Return the object pointed to by the "next" address pointer of the
91
+ # provided object.
92
+ def next(object)
93
+ raise "Class #{object.class} does not respond to next_address" unless object.respond_to?(:next_address)
93
94
 
94
- # Return the number of items in the list.
95
- def length
96
- @base[:length]
97
- end
95
+ object_from_address(object.next_address)
96
+ end
98
97
 
99
- # Return the first object in the list using the list base node "first"
100
- # address pointer.
101
- def first
102
- object_from_address(@base[:first])
103
- end
98
+ # Return the number of items in the list.
99
+ def length
100
+ @base.length
101
+ end
104
102
 
105
- # Return the first object in the list using the list base node "last"
106
- # address pointer.
107
- def last
108
- object_from_address(@base[:last])
109
- end
103
+ # Is the list currently empty?
104
+ def empty?
105
+ length.zero?
106
+ end
110
107
 
111
- # Return a list cursor for the list.
112
- def list_cursor(node=:min, direction=:forward)
113
- ListCursor.new(self, node, direction)
114
- end
108
+ # Return the first object in the list using the list base node "first"
109
+ # address pointer.
110
+ def first
111
+ object_from_address(@base.first)
112
+ end
115
113
 
116
- # Return whether the given item is present in the list. This depends on the
117
- # item and the items in the list implementing some sufficient == method.
118
- # This is implemented rather inefficiently by constructing an array and
119
- # leaning on Array#include? to do the real work.
120
- def include?(item)
121
- each.to_a.include? item
122
- end
114
+ # Return the first object in the list using the list base node "last"
115
+ # address pointer.
116
+ def last
117
+ object_from_address(@base.last)
118
+ end
123
119
 
124
- # Iterate through all nodes in the list.
125
- def each
126
- unless block_given?
127
- return enum_for(:each)
120
+ # Return a list cursor for the list.
121
+ def list_cursor(node = :min, direction = :forward)
122
+ ListCursor.new(self, node, direction)
128
123
  end
129
124
 
130
- list_cursor.each_node do |node|
131
- yield node
125
+ # Return whether the given item is present in the list. This depends on the
126
+ # item and the items in the list implementing some sufficient == method.
127
+ # This is implemented rather inefficiently by constructing an array and
128
+ # leaning on Array#include? to do the real work.
129
+ def include?(item)
130
+ each.to_a.include?(item)
132
131
  end
133
- end
134
132
 
135
- # A list iteration cursor used primarily by the Innodb::List #cursor method
136
- # implicitly. Keeps its own state for iterating through lists efficiently.
137
- class ListCursor
138
- def initialize(list, node=:min, direction=:forward)
139
- @initial = true
140
- @list = list
141
- @direction = direction
142
-
143
- case node
144
- when :min
145
- @node = @list.first
146
- when :max
147
- @node = @list.last
148
- else
149
- @node = node
150
- end
133
+ # Iterate through all nodes in the list.
134
+ def each(&block)
135
+ return enum_for(:each) unless block_given?
136
+
137
+ list_cursor.each_node(&block)
151
138
  end
152
139
 
153
- def node
154
- if @initial
155
- @initial = false
156
- return @node
140
+ # A list iteration cursor used primarily by the Innodb::List #cursor method
141
+ # implicitly. Keeps its own state for iterating through lists efficiently.
142
+ class ListCursor
143
+ def initialize(list, node = :min, direction = :forward)
144
+ @initial = true
145
+ @list = list
146
+ @direction = direction
147
+ @node = initial_node(node)
157
148
  end
158
149
 
159
- case @direction
160
- when :forward
161
- next_node
162
- when :backward
163
- prev_node
150
+ def initial_node(node)
151
+ case node
152
+ when :min
153
+ @list.first
154
+ when :max
155
+ @list.last
156
+ else
157
+ node
158
+ end
164
159
  end
165
- end
166
160
 
167
- # Return the previous entry from the current position, and advance the
168
- # cursor position to the returned entry. If the cursor is currently nil,
169
- # return the last entry in the list and adjust the cursor position to
170
- # that entry.
171
- def prev_node
172
- if node = @list.prev(@node)
173
- @node = node
161
+ def node
162
+ if @initial
163
+ @initial = false
164
+ return @node
165
+ end
166
+
167
+ case @direction
168
+ when :forward
169
+ next_node
170
+ when :backward
171
+ prev_node
172
+ end
174
173
  end
175
- end
176
174
 
177
- # Return the next entry from the current position, and advance the
178
- # cursor position to the returned entry. If the cursor is currently nil,
179
- # return the first entry in the list and adjust the cursor position to
180
- # that entry.
181
- def next_node
182
- if node = @list.next(@node)
183
- @node = node
175
+ def goto_node(node)
176
+ @node = node if node
184
177
  end
185
- end
186
178
 
187
- def each_node
188
- unless block_given?
189
- return enum_for(:each_node)
179
+ # Return the previous entry from the current position, and advance the
180
+ # cursor position to the returned entry. If the cursor is currently nil,
181
+ # return the last entry in the list and adjust the cursor position to
182
+ # that entry.
183
+ def prev_node
184
+ goto_node(@list.prev(@node))
190
185
  end
191
186
 
192
- while n = node
193
- yield n
187
+ # Return the next entry from the current position, and advance the
188
+ # cursor position to the returned entry. If the cursor is currently nil,
189
+ # return the first entry in the list and adjust the cursor position to
190
+ # that entry.
191
+ def next_node
192
+ goto_node(@list.next(@node))
193
+ end
194
+
195
+ def each_node
196
+ return enum_for(:each_node) unless block_given?
197
+
198
+ while (n = node)
199
+ yield n
200
+ end
194
201
  end
195
202
  end
196
- end
197
- end
198
203
 
199
- # A list of extent descriptor entries. Objects returned by list methods
200
- # will be Innodb::Xdes objects.
201
- class Innodb::List::Xdes < Innodb::List
202
- def object_from_address(address)
203
- if address && page = @space.page(address[:page])
204
- Innodb::Xdes.new(page, page.cursor(address[:offset] - 8))
204
+ # A list of extent descriptor entries. Objects returned by list methods
205
+ # will be Innodb::Xdes objects.
206
+ class Xdes < Innodb::List
207
+ def object_from_address(address)
208
+ return unless address
209
+
210
+ page = @space.page(address.page)
211
+ return unless page
212
+
213
+ Innodb::Xdes.new(page, page.cursor(address.offset - 8))
214
+ end
205
215
  end
206
- end
207
- end
208
216
 
209
- # A list of Inode pages. Objects returned by list methods will be
210
- # Innodb::Page::Inode objects.
211
- class Innodb::List::Inode < Innodb::List
212
- def object_from_address(address)
213
- if address && page = @space.page(address[:page])
214
- page
217
+ # A list of Inode pages. Objects returned by list methods will be
218
+ # Innodb::Page::Inode objects.
219
+ class Inode < Innodb::List
220
+ def object_from_address(address)
221
+ return unless address
222
+
223
+ @space.page(address.page)
224
+ end
215
225
  end
216
- end
217
- end
218
226
 
219
- class Innodb::List::UndoPage < Innodb::List
220
- def object_from_address(address)
221
- if address && page = @space.page(address[:page])
222
- page
227
+ class UndoPage < Innodb::List
228
+ def object_from_address(address)
229
+ return unless address
230
+
231
+ @space.page(address.page)
232
+ end
223
233
  end
224
- end
225
- end
226
234
 
227
- class Innodb::List::History < Innodb::List
228
- def object_from_address(address)
229
- if address && page = @space.page(address[:page])
230
- Innodb::UndoLog.new(page, address[:offset] - 34)
235
+ class History < Innodb::List
236
+ def object_from_address(address)
237
+ return unless address
238
+
239
+ page = @space.page(address.page)
240
+ return unless page
241
+
242
+ Innodb::UndoLog.new(page, address.offset - 34)
243
+ end
231
244
  end
232
245
  end
233
246
  end