innodb_ruby 0.9.13 → 0.11.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 +14 -19
  4. data/bin/innodb_space +592 -745
  5. data/lib/innodb.rb +5 -5
  6. data/lib/innodb/checksum.rb +26 -24
  7. data/lib/innodb/data_dictionary.rb +490 -550
  8. data/lib/innodb/data_type.rb +362 -325
  9. data/lib/innodb/field.rb +102 -89
  10. data/lib/innodb/fseg_entry.rb +22 -26
  11. data/lib/innodb/history.rb +21 -21
  12. data/lib/innodb/history_list.rb +72 -76
  13. data/lib/innodb/ibuf_bitmap.rb +36 -36
  14. data/lib/innodb/ibuf_index.rb +6 -2
  15. data/lib/innodb/index.rb +245 -275
  16. data/lib/innodb/inode.rb +166 -124
  17. data/lib/innodb/list.rb +191 -183
  18. data/lib/innodb/log.rb +139 -110
  19. data/lib/innodb/log_block.rb +100 -91
  20. data/lib/innodb/log_group.rb +53 -64
  21. data/lib/innodb/log_reader.rb +97 -96
  22. data/lib/innodb/log_record.rb +328 -279
  23. data/lib/innodb/lsn.rb +86 -81
  24. data/lib/innodb/page.rb +446 -291
  25. data/lib/innodb/page/blob.rb +82 -83
  26. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  27. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  28. data/lib/innodb/page/index.rb +965 -924
  29. data/lib/innodb/page/index_compressed.rb +34 -34
  30. data/lib/innodb/page/inode.rb +103 -112
  31. data/lib/innodb/page/sys.rb +13 -15
  32. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  33. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  34. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  35. data/lib/innodb/page/trx_sys.rb +204 -182
  36. data/lib/innodb/page/undo_log.rb +106 -92
  37. data/lib/innodb/record.rb +121 -164
  38. data/lib/innodb/record_describer.rb +66 -68
  39. data/lib/innodb/space.rb +386 -391
  40. data/lib/innodb/stats.rb +33 -35
  41. data/lib/innodb/system.rb +149 -171
  42. data/lib/innodb/undo_log.rb +129 -107
  43. data/lib/innodb/undo_record.rb +255 -247
  44. data/lib/innodb/util/buffer_cursor.rb +81 -79
  45. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  46. data/lib/innodb/version.rb +2 -2
  47. data/lib/innodb/xdes.rb +144 -142
  48. metadata +112 -21
@@ -1,49 +1,49 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- class Innodb::IbufBitmap
4
- BITS_PER_PAGE = 4
3
+ module Innodb
4
+ class IbufBitmap
5
+ PageStatus = Struct.new(:free, :buffered, :ibuf, keyword_init: true)
5
6
 
6
- BITMAP_BV_FREE = 1 + 2
7
- BITMAP_BV_BUFFERED = 4
8
- BITMAP_BV_IBUF = 8
7
+ BITS_PER_PAGE = 4
9
8
 
10
- BITMAP_BV_ALL =
11
- BITMAP_BV_FREE |
12
- BITMAP_BV_BUFFERED |
13
- BITMAP_BV_IBUF
9
+ BITMAP_BV_FREE = 1 + 2
10
+ BITMAP_BV_BUFFERED = 4
11
+ BITMAP_BV_IBUF = 8
14
12
 
15
- def initialize(page, cursor)
16
- @page = page
17
- @bitmap = read_bitmap(page, cursor)
18
- end
13
+ BITMAP_BV_ALL =
14
+ BITMAP_BV_FREE |
15
+ BITMAP_BV_BUFFERED |
16
+ BITMAP_BV_IBUF
19
17
 
20
- def size_bitmap
21
- (@page.space.pages_per_bookkeeping_page * BITS_PER_PAGE) / 8
22
- end
18
+ def initialize(page, cursor)
19
+ @page = page
20
+ @bitmap = read_bitmap(cursor)
21
+ end
23
22
 
24
- def read_bitmap(page, cursor)
25
- cursor.name("ibuf_bitmap") do |c|
26
- c.get_bytes(size_bitmap)
23
+ def size_bitmap
24
+ (@page.space.pages_per_bookkeeping_page * BITS_PER_PAGE) / 8
27
25
  end
28
- end
29
26
 
30
- def each_page_status
31
- unless block_given?
32
- return enum_for(:each_page_status)
27
+ def read_bitmap(cursor)
28
+ cursor.name("ibuf_bitmap") { |c| c.read_bytes(size_bitmap) }
33
29
  end
34
30
 
35
- bitmap = @bitmap.enum_for(:each_byte)
36
-
37
- bitmap.each_with_index do |byte, byte_index|
38
- (0..1).each do |page_offset|
39
- page_number = (byte_index * 2) + page_offset
40
- page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
41
- page_status = {
42
- :free => (page_bits & BITMAP_BV_FREE),
43
- :buffered => (page_bits & BITMAP_BV_BUFFERED != 0),
44
- :ibuf => (page_bits & BITMAP_BV_IBUF != 0),
45
- }
46
- yield page_number, page_status
31
+ def each_page_status
32
+ return enum_for(:each_page_status) unless block_given?
33
+
34
+ bitmap = @bitmap.enum_for(:each_byte)
35
+
36
+ bitmap.each_with_index do |byte, byte_index|
37
+ (0..1).each do |page_offset|
38
+ page_number = (byte_index * 2) + page_offset
39
+ page_bits = ((byte >> (page_offset * BITS_PER_PAGE)) & BITMAP_BV_ALL)
40
+ page_status = PageStatus.new(
41
+ free: (page_bits & BITMAP_BV_FREE),
42
+ buffered: (page_bits & BITMAP_BV_BUFFERED != 0),
43
+ ibuf: (page_bits & BITMAP_BV_IBUF != 0)
44
+ )
45
+ yield page_number, page_status
46
+ end
47
47
  end
48
48
  end
49
49
  end
@@ -1,3 +1,7 @@
1
- class Innodb::IbufIndex
2
- INDEX_ID = 0xFFFFFFFF00000000
1
+ # frozen_string_literal: true
2
+
3
+ module Innodb
4
+ class IbufIndex
5
+ INDEX_ID = 0xFFFFFFFF00000000
6
+ end
3
7
  end
data/lib/innodb/index.rb CHANGED
@@ -1,361 +1,331 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # An InnoDB index B-tree, given an Innodb::Space and a root page number.
4
- class Innodb::Index
5
- attr_reader :root
6
- attr_reader :space
7
- attr_accessor :record_describer
4
+ module Innodb
5
+ class Index
6
+ attr_reader :root
7
+ attr_reader :space
8
+ attr_accessor :record_describer
8
9
 
9
- def initialize(space, root_page_number, record_describer=nil)
10
- @space = space
11
- @record_describer = record_describer || space.record_describer
10
+ FSEG_LIST_NAMES = %i[
11
+ internal
12
+ leaf
13
+ ].freeze
12
14
 
13
- @root = page(root_page_number)
15
+ def initialize(space, root_page_number, record_describer = nil)
16
+ @space = space
17
+ @record_describer = record_describer || space.record_describer
14
18
 
15
- unless @root
16
- raise "Page #{root_page_number} couldn't be read"
17
- end
19
+ @root = page(root_page_number)
18
20
 
19
- # The root page should be an index page.
20
- unless @root.type == :INDEX
21
- raise "Page #{root_page_number} is a #{@root.type} page, not an INDEX page"
22
- end
21
+ raise "Page #{root_page_number} couldn't be read" unless @root
23
22
 
24
- # The root page should be the only page at its level.
25
- unless @root.prev.nil? && @root.next.nil?
26
- raise "Page #{root_page_number} is a node page, but not appear to be the root; it has previous page and next page pointers"
27
- end
28
- end
23
+ # The root page should be an index page.
24
+ raise "Page #{root_page_number} is a #{@root.type} page, not an INDEX page" unless @root.type == :INDEX
29
25
 
30
- def page(page_number)
31
- page = @space.page(page_number)
32
- page.record_describer = @record_describer
33
- page
34
- end
26
+ # The root page should be the only page at its level.
27
+ raise "Page #{root_page_number} does not appear to be an index root" if @root.prev || @root.next
28
+ end
35
29
 
36
- # A helper function to access the index ID in the page header.
37
- def id
38
- @root.page_header[:index_id]
39
- end
30
+ def page(page_number)
31
+ page = @space.page(page_number)
32
+ raise "Page #{page_number} couldn't be read" unless page
40
33
 
41
- # Return the type of node that the given page represents in the index tree.
42
- def node_type(page)
43
- if @root.offset == page.offset
44
- :root
45
- elsif page.level == 0
46
- :leaf
47
- else
48
- :internal
34
+ page.record_describer = @record_describer
35
+ page
49
36
  end
50
- end
51
37
 
52
- # Internal method used by recurse.
53
- def _recurse(parent_page, page_proc, link_proc, depth=0)
54
- if page_proc && parent_page.type == :INDEX
55
- page_proc.call(parent_page, depth)
38
+ # A helper function to access the index ID in the page header.
39
+ def id
40
+ @root.page_header.index_id
56
41
  end
57
42
 
58
- parent_page.each_child_page do |child_page_number, child_min_key|
59
- child_page = page(child_page_number)
60
- child_page.record_describer = record_describer
61
- if child_page.type == :INDEX
62
- if link_proc
63
- link_proc.call(parent_page, child_page, child_min_key, depth+1)
64
- end
65
- _recurse(child_page, page_proc, link_proc, depth+1)
43
+ # Return the type of node that the given page represents in the index tree.
44
+ def node_type(page)
45
+ if @root.offset == page.offset
46
+ :root
47
+ elsif page.level.zero?
48
+ :leaf
49
+ else
50
+ :internal
66
51
  end
67
52
  end
68
- end
69
53
 
70
- # Walk an index tree depth-first, calling procs for each page and link
71
- # in the tree.
72
- def recurse(page_proc, link_proc)
73
- _recurse(@root, page_proc, link_proc)
74
- end
54
+ # Internal method used by recurse.
55
+ def _recurse(parent_page, page_proc, link_proc, depth = 0)
56
+ page_proc.call(parent_page, depth) if page_proc && parent_page.type == :INDEX
75
57
 
76
- # Return the first leaf page in the index by walking down the left side
77
- # of the B-tree until a page at the given level is encountered.
78
- def min_page_at_level(level)
79
- page = @root
80
- record = @root.min_record
81
- while record && page.level > level
82
- page = page(record.child_page_number)
83
- record = page.min_record
84
- end
85
- page if page.level == level
86
- end
58
+ parent_page.each_child_page do |child_page_number, child_min_key|
59
+ child_page = page(child_page_number)
60
+ child_page.record_describer = record_describer
61
+ next unless child_page.type == :INDEX
87
62
 
88
- # Return the minimum record in the index.
89
- def min_record
90
- if min_page = min_page_at_level(0)
91
- min_page.min_record
63
+ link_proc&.call(parent_page, child_page, child_min_key, depth + 1)
64
+ _recurse(child_page, page_proc, link_proc, depth + 1)
65
+ end
92
66
  end
93
- end
94
67
 
95
- # Return the last leaf page in the index by walking down the right side
96
- # of the B-tree until a page at the given level is encountered.
97
- def max_page_at_level(level)
98
- page = @root
99
- record = @root.max_record
100
- while record && page.level > level
101
- page = page(record.child_page_number)
102
- record = page.max_record
68
+ # Walk an index tree depth-first, calling procs for each page and link
69
+ # in the tree.
70
+ def recurse(page_proc, link_proc)
71
+ _recurse(@root, page_proc, link_proc)
103
72
  end
104
- page if page.level == level
105
- end
106
73
 
107
- # Return the maximum record in the index.
108
- def max_record
109
- if max_page = max_page_at_level(0)
110
- max_page.max_record
74
+ # Return the first leaf page in the index by walking down the left side
75
+ # of the B-tree until a page at the given level is encountered.
76
+ def min_page_at_level(level)
77
+ page = @root
78
+ record = @root.min_record
79
+ while record && page.level > level
80
+ page = page(record.child_page_number)
81
+ record = page.min_record
82
+ end
83
+ page if page.level == level
111
84
  end
112
- end
113
85
 
114
- # Return the file segment with the given name from the fseg header.
115
- def fseg(name)
116
- @root.fseg_header[name]
117
- end
118
-
119
- def field_names
120
- record_describer.field_names
121
- end
122
-
123
- # Iterate through all file segments in the index.
124
- def each_fseg
125
- unless block_given?
126
- return enum_for(:each_fseg)
86
+ # Return the minimum record in the index.
87
+ def min_record
88
+ min_page_at_level(0)&.min_record
127
89
  end
128
90
 
129
- [:internal, :leaf].each do |fseg_name|
130
- yield fseg_name, fseg(fseg_name)
91
+ # Return the last leaf page in the index by walking down the right side
92
+ # of the B-tree until a page at the given level is encountered.
93
+ def max_page_at_level(level)
94
+ page = @root
95
+ record = @root.max_record
96
+ while record && page.level > level
97
+ page = page(record.child_page_number)
98
+ record = page.max_record
99
+ end
100
+ page if page.level == level
131
101
  end
132
- end
133
102
 
134
- # Iterate through all lists in a given fseg.
135
- def each_fseg_list(fseg)
136
- unless block_given?
137
- return enum_for(:each_fseg_list, fseg)
103
+ # Return the maximum record in the index.
104
+ def max_record
105
+ max_page_at_level(0)&.max_record
138
106
  end
139
107
 
140
- fseg.each_list do |list_name, list|
141
- yield list_name, list
108
+ # Return the file segment with the given name from the fseg header.
109
+ def fseg(name)
110
+ @root.fseg_header[name]
142
111
  end
143
- end
144
112
 
145
- # Iterate through all frag pages in a given fseg.
146
- def each_fseg_frag_page(fseg)
147
- unless block_given?
148
- return enum_for(:each_fseg_frag_page, fseg)
113
+ def field_names
114
+ record_describer.field_names
149
115
  end
150
116
 
151
- fseg.frag_array_pages.each do |page_number|
152
- yield page_number, page(page_number)
153
- end
154
- end
117
+ # Iterate through all file segments in the index.
118
+ def each_fseg
119
+ return enum_for(:each_fseg) unless block_given?
155
120
 
156
- # Iterate through all pages at this level starting with the provided page.
157
- def each_page_from(page)
158
- unless block_given?
159
- return enum_for(:each_page_from, page)
121
+ FSEG_LIST_NAMES.each do |fseg_name|
122
+ yield fseg_name, fseg(fseg_name)
123
+ end
160
124
  end
161
125
 
162
- while page && page.type == :INDEX
163
- yield page
164
- break unless page.next
165
- page = page(page.next)
166
- end
167
- end
126
+ # Iterate through all lists in a given fseg.
127
+ def each_fseg_list(fseg, &block)
128
+ return enum_for(:each_fseg_list, fseg) unless block_given?
168
129
 
169
- # Iterate through all pages at the given level by finding the first page
170
- # and following the next pointers in each page.
171
- def each_page_at_level(level)
172
- unless block_given?
173
- return enum_for(:each_page_at_level, level)
130
+ fseg.each_list(&block)
174
131
  end
175
132
 
176
- each_page_from(min_page_at_level(level)) { |page| yield page }
177
- end
178
-
179
- # Iterate through all records on all leaf pages in ascending order.
180
- def each_record
181
- unless block_given?
182
- return enum_for(:each_record)
183
- end
133
+ # Iterate through all frag pages in a given fseg.
134
+ def each_fseg_frag_page(fseg)
135
+ return enum_for(:each_fseg_frag_page, fseg) unless block_given?
184
136
 
185
- each_page_at_level(0) do |page|
186
- page.each_record do |record|
187
- yield record
137
+ fseg.frag_array_pages.each do |page_number|
138
+ yield page_number, page(page_number)
188
139
  end
189
140
  end
190
- end
191
141
 
192
- # Search for a record within the entire index, walking down the non-leaf
193
- # pages until a leaf page is found, and then verifying that the record
194
- # returned on the leaf page is an exact match for the key. If a matching
195
- # record is not found, nil is returned (either because linear_search_in_page
196
- # returns nil breaking the loop, or because compare_key returns non-zero).
197
- def linear_search(key)
198
- Innodb::Stats.increment :linear_search
199
-
200
- page = @root
201
-
202
- if Innodb.debug?
203
- puts "linear_search: root=%i, level=%i, key=(%s)" % [
204
- page.offset,
205
- page.level,
206
- key.join(", "),
207
- ]
208
- end
142
+ # Iterate through all pages at this level starting with the provided page.
143
+ def each_page_from(page)
144
+ return enum_for(:each_page_from, page) unless block_given?
209
145
 
210
- while rec =
211
- page.linear_search_from_cursor(page.record_cursor(page.infimum.next), key)
212
- if page.level > 0
213
- # If we haven't reached a leaf page yet, move down the tree and search
214
- # again using linear search.
215
- page = page(rec.child_page_number)
216
- else
217
- # We're on a leaf page, so return the page and record if there is a
218
- # match. If there is no match, break the loop and cause nil to be
219
- # returned.
220
- return rec if rec.compare_key(key) == 0
221
- break
146
+ while page && page.type == :INDEX
147
+ yield page
148
+ break unless page.next
149
+
150
+ page = page(page.next)
222
151
  end
223
152
  end
224
- end
225
153
 
226
- # Search for a record within the entire index like linear_search, but use
227
- # the page directory to search while making as few record comparisons as
228
- # possible. If a matching record is not found, nil is returned.
229
- def binary_search(key)
230
- Innodb::Stats.increment :binary_search
154
+ # Iterate through all pages at the given level by finding the first page
155
+ # and following the next pointers in each page.
156
+ def each_page_at_level(level, &block)
157
+ return enum_for(:each_page_at_level, level) unless block_given?
231
158
 
232
- page = @root
233
-
234
- if Innodb.debug?
235
- puts "binary_search: root=%i, level=%i, key=(%s)" % [
236
- page.offset,
237
- page.level,
238
- key.join(", "),
239
- ]
159
+ each_page_from(min_page_at_level(level), &block)
240
160
  end
241
161
 
242
- # Remove supremum from the page directory, since nothing can be scanned
243
- # linearly from there anyway.
244
- while rec = page.binary_search_by_directory(page.directory[0...-1], key)
245
- if page.level > 0
246
- # If we haven't reached a leaf page yet, move down the tree and search
247
- # again using binary search.
248
- page = page(rec.child_page_number)
249
- else
250
- # We're on a leaf page, so return the page and record if there is a
251
- # match. If there is no match, break the loop and cause nil to be
252
- # returned.
253
- return rec if rec.compare_key(key) == 0
254
- break
255
- end
256
- end
257
- end
162
+ # Iterate through all records on all leaf pages in ascending order.
163
+ def each_record(&block)
164
+ return enum_for(:each_record) unless block_given?
258
165
 
259
- # A cursor to walk the index (cursor) forwards or backward starting with
260
- # a given record, or the minimum (:min) or maximum (:max) record in the
261
- # index.
262
- class IndexCursor
263
- def initialize(index, record, direction)
264
- Innodb::Stats.increment :index_cursor_create
265
- @index = index
266
- @direction = direction
267
- case record
268
- when :min
269
- # Start at the minimum record on the minimum page in the index.
270
- @page = index.min_page_at_level(0)
271
- @page_cursor = @page.record_cursor(:min, direction)
272
- when :max
273
- # Start at the maximum record on the maximum page in the index.
274
- @page = index.max_page_at_level(0)
275
- @page_cursor = @page.record_cursor(:max, direction)
276
- else
277
- # Start at the record provided.
278
- @page = record.page
279
- @page_cursor = @page.record_cursor(record.offset, direction)
166
+ each_page_at_level(0) do |page|
167
+ page.each_record(&block)
280
168
  end
281
169
  end
282
170
 
283
- # Return the next record in the order defined when the cursor was created.
284
- def record
285
- if rec = @page_cursor.record
286
- return rec
171
+ # Search for a record within the entire index, walking down the non-leaf
172
+ # pages until a leaf page is found, and then verifying that the record
173
+ # returned on the leaf page is an exact match for the key. If a matching
174
+ # record is not found, nil is returned (either because linear_search_in_page
175
+ # returns nil breaking the loop, or because compare_key returns non-zero).
176
+ def linear_search(key)
177
+ Innodb::Stats.increment :linear_search
178
+
179
+ page = @root
180
+
181
+ if Innodb.debug?
182
+ puts "linear_search: root=%i, level=%i, key=(%s)" % [
183
+ page.offset,
184
+ page.level,
185
+ key.join(", "),
186
+ ]
287
187
  end
288
188
 
289
- case @direction
290
- when :forward
291
- next_record
292
- when :backward
293
- prev_record
189
+ while (rec = page.linear_search_from_cursor(page.record_cursor(page.infimum.next), key))
190
+ if page.level.positive?
191
+ # If we haven't reached a leaf page yet, move down the tree and search
192
+ # again using linear search.
193
+ page = page(rec.child_page_number)
194
+ else
195
+ # We're on a leaf page, so return the page and record if there is a
196
+ # match. If there is no match, break the loop and cause nil to be
197
+ # returned.
198
+ return rec if rec.compare_key(key).zero?
199
+
200
+ break
201
+ end
294
202
  end
295
203
  end
296
204
 
297
- # Iterate through all records in the cursor.
298
- def each_record
299
- unless block_given?
300
- return enum_for(:each_record)
205
+ # Search for a record within the entire index like linear_search, but use
206
+ # the page directory to search while making as few record comparisons as
207
+ # possible. If a matching record is not found, nil is returned.
208
+ def binary_search(key)
209
+ Innodb::Stats.increment :binary_search
210
+
211
+ page = @root
212
+
213
+ if Innodb.debug?
214
+ puts "binary_search: root=%i, level=%i, key=(%s)" % [
215
+ page.offset,
216
+ page.level,
217
+ key.join(", "),
218
+ ]
301
219
  end
302
220
 
303
- while rec = record
304
- yield rec
221
+ # Remove supremum from the page directory, since nothing can be scanned
222
+ # linearly from there anyway.
223
+ while (rec = page.binary_search_by_directory(page.directory[0...-1], key))
224
+ if page.level.positive?
225
+ # If we haven't reached a leaf page yet, move down the tree and search
226
+ # again using binary search.
227
+ page = page(rec.child_page_number)
228
+ else
229
+ # We're on a leaf page, so return the page and record if there is a
230
+ # match. If there is no match, break the loop and cause nil to be
231
+ # returned.
232
+ return rec if rec.compare_key(key).zero?
233
+
234
+ break
235
+ end
305
236
  end
306
237
  end
307
238
 
308
- private
309
-
310
- # Move the cursor to a new starting position in a given page.
311
- def move_cursor(page, record)
312
- unless @page = @index.page(page)
313
- raise "Failed to load page"
239
+ # A cursor to walk the index (cursor) forwards or backward starting with
240
+ # a given record, or the minimum (:min) or maximum (:max) record in the
241
+ # index.
242
+ class IndexCursor
243
+ def initialize(index, record, direction)
244
+ Innodb::Stats.increment :index_cursor_create
245
+ @index = index
246
+ @direction = direction
247
+ case record
248
+ when :min
249
+ # Start at the minimum record on the minimum page in the index.
250
+ @page = index.min_page_at_level(0)
251
+ @page_cursor = @page.record_cursor(:min, direction)
252
+ when :max
253
+ # Start at the maximum record on the maximum page in the index.
254
+ @page = index.max_page_at_level(0)
255
+ @page_cursor = @page.record_cursor(:max, direction)
256
+ else
257
+ # Start at the record provided.
258
+ @page = record.page
259
+ @page_cursor = @page.record_cursor(record.offset, direction)
260
+ end
314
261
  end
315
262
 
316
- unless @page_cursor = @page.record_cursor(record, @direction)
317
- raise "Failed to position cursor"
263
+ # Return the next record in the order defined when the cursor was created.
264
+ def record
265
+ if (rec = @page_cursor.record)
266
+ return rec
267
+ end
268
+
269
+ case @direction
270
+ when :forward
271
+ next_record
272
+ when :backward
273
+ prev_record
274
+ end
318
275
  end
319
- end
320
276
 
321
- # Move to the next record in the forward direction and return it.
322
- def next_record
323
- Innodb::Stats.increment :index_cursor_next_record
277
+ # Iterate through all records in the cursor.
278
+ def each_record
279
+ return enum_for(:each_record) unless block_given?
324
280
 
325
- if rec = @page_cursor.record
326
- return rec
281
+ while (rec = record)
282
+ yield rec
283
+ end
327
284
  end
328
285
 
329
- unless next_page = @page.next
330
- return nil
286
+ private
287
+
288
+ # Move the cursor to a new starting position in a given page.
289
+ def move_cursor(page, record)
290
+ raise "Failed to load page" unless (@page = @index.page(page))
291
+ raise "Failed to position cursor" unless (@page_cursor = @page.record_cursor(record, @direction))
331
292
  end
332
293
 
333
- move_cursor(next_page, :min)
294
+ # Move to the next record in the forward direction and return it.
295
+ def next_record
296
+ Innodb::Stats.increment :index_cursor_next_record
334
297
 
335
- @page_cursor.record
336
- end
298
+ if (rec = @page_cursor.record)
299
+ return rec
300
+ end
337
301
 
338
- # Move to the previous record in the backward direction and return it.
339
- def prev_record
340
- Innodb::Stats.increment :index_cursor_prev_record
302
+ return unless (next_page = @page.next)
341
303
 
342
- if rec = @page_cursor.record
343
- return rec
344
- end
304
+ move_cursor(next_page, :min)
345
305
 
346
- unless prev_page = @page.prev
347
- return nil
306
+ @page_cursor.record
348
307
  end
349
308
 
350
- move_cursor(prev_page, :max)
309
+ # Move to the previous record in the backward direction and return it.
310
+ def prev_record
311
+ Innodb::Stats.increment :index_cursor_prev_record
312
+
313
+ if (rec = @page_cursor.record)
314
+ return rec
315
+ end
316
+
317
+ return unless (prev_page = @page.prev)
351
318
 
352
- @page_cursor.record
319
+ move_cursor(prev_page, :max)
320
+
321
+ @page_cursor.record
322
+ end
353
323
  end
354
- end
355
324
 
356
- # Return an IndexCursor starting at the given record (an Innodb::Record,
357
- # :min, or :max) and cursor in the direction given (:forward or :backward).
358
- def cursor(record=:min, direction=:forward)
359
- IndexCursor.new(self, record, direction)
325
+ # Return an IndexCursor starting at the given record (an Innodb::Record,
326
+ # :min, or :max) and cursor in the direction given (:forward or :backward).
327
+ def cursor(record = :min, direction = :forward)
328
+ IndexCursor.new(self, record, direction)
329
+ end
360
330
  end
361
331
  end