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.
- checksums.yaml +7 -0
- data/README.md +5 -6
- data/bin/innodb_log +14 -19
- data/bin/innodb_space +592 -745
- data/lib/innodb.rb +5 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -325
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -275
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +191 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page.rb +446 -291
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +965 -924
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +386 -391
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- metadata +112 -21
data/lib/innodb/ibuf_bitmap.rb
CHANGED
@@ -1,49 +1,49 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module Innodb
|
4
|
+
class IbufBitmap
|
5
|
+
PageStatus = Struct.new(:free, :buffered, :ibuf, keyword_init: true)
|
5
6
|
|
6
|
-
|
7
|
-
BITMAP_BV_BUFFERED = 4
|
8
|
-
BITMAP_BV_IBUF = 8
|
7
|
+
BITS_PER_PAGE = 4
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
BITMAP_BV_IBUF
|
9
|
+
BITMAP_BV_FREE = 1 + 2
|
10
|
+
BITMAP_BV_BUFFERED = 4
|
11
|
+
BITMAP_BV_IBUF = 8
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
BITMAP_BV_ALL =
|
14
|
+
BITMAP_BV_FREE |
|
15
|
+
BITMAP_BV_BUFFERED |
|
16
|
+
BITMAP_BV_IBUF
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
def initialize(page, cursor)
|
19
|
+
@page = page
|
20
|
+
@bitmap = read_bitmap(cursor)
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
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
|
-
|
31
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
data/lib/innodb/ibuf_index.rb
CHANGED
data/lib/innodb/index.rb
CHANGED
@@ -1,361 +1,331 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# An InnoDB index B-tree, given an Innodb::Space and a root page number.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module Innodb
|
5
|
+
class Index
|
6
|
+
attr_reader :root
|
7
|
+
attr_reader :space
|
8
|
+
attr_accessor :record_describer
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
FSEG_LIST_NAMES = %i[
|
11
|
+
internal
|
12
|
+
leaf
|
13
|
+
].freeze
|
12
14
|
|
13
|
-
|
15
|
+
def initialize(space, root_page_number, record_describer = nil)
|
16
|
+
@space = space
|
17
|
+
@record_describer = record_describer || space.record_describer
|
14
18
|
|
15
|
-
|
16
|
-
raise "Page #{root_page_number} couldn't be read"
|
17
|
-
end
|
19
|
+
@root = page(root_page_number)
|
18
20
|
|
19
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
130
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
141
|
-
|
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
|
-
|
146
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
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
|
-
|
177
|
-
|
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
|
-
|
186
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
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
|
-
|
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
|
-
#
|
243
|
-
|
244
|
-
|
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
|
-
|
260
|
-
|
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
|
-
#
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
-
#
|
298
|
-
|
299
|
-
|
300
|
-
|
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
|
-
|
304
|
-
|
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
|
-
|
309
|
-
|
310
|
-
#
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
-
|
317
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
277
|
+
# Iterate through all records in the cursor.
|
278
|
+
def each_record
|
279
|
+
return enum_for(:each_record) unless block_given?
|
324
280
|
|
325
|
-
|
326
|
-
|
281
|
+
while (rec = record)
|
282
|
+
yield rec
|
283
|
+
end
|
327
284
|
end
|
328
285
|
|
329
|
-
|
330
|
-
|
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
|
-
|
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
|
-
|
336
|
-
|
298
|
+
if (rec = @page_cursor.record)
|
299
|
+
return rec
|
300
|
+
end
|
337
301
|
|
338
|
-
|
339
|
-
def prev_record
|
340
|
-
Innodb::Stats.increment :index_cursor_prev_record
|
302
|
+
return unless (next_page = @page.next)
|
341
303
|
|
342
|
-
|
343
|
-
return rec
|
344
|
-
end
|
304
|
+
move_cursor(next_page, :min)
|
345
305
|
|
346
|
-
|
347
|
-
return nil
|
306
|
+
@page_cursor.record
|
348
307
|
end
|
349
308
|
|
350
|
-
|
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
|
-
|
319
|
+
move_cursor(prev_page, :max)
|
320
|
+
|
321
|
+
@page_cursor.record
|
322
|
+
end
|
353
323
|
end
|
354
|
-
end
|
355
324
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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
|