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.
- checksums.yaml +7 -0
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +654 -778
- 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 -276
- data/lib/innodb/inode.rb +166 -124
- data/lib/innodb/list.rb +196 -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/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/page.rb +417 -414
- data/lib/innodb/record.rb +121 -164
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +381 -413
- 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
- data/lib/innodb.rb +4 -5
- metadata +100 -25
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,362 +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.record_describer = @record_describer
|
34
|
-
page
|
35
|
-
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
|
36
29
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
30
|
+
def page(page_number)
|
31
|
+
page = @space.page(page_number)
|
32
|
+
raise "Page #{page_number} couldn't be read" unless page
|
41
33
|
|
42
|
-
|
43
|
-
|
44
|
-
if @root.offset == page.offset
|
45
|
-
:root
|
46
|
-
elsif page.level == 0
|
47
|
-
:leaf
|
48
|
-
else
|
49
|
-
:internal
|
34
|
+
page.record_describer = @record_describer
|
35
|
+
page
|
50
36
|
end
|
51
|
-
end
|
52
37
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
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
|
57
41
|
end
|
58
42
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
67
51
|
end
|
68
52
|
end
|
69
|
-
end
|
70
53
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
_recurse(@root, page_proc, link_proc)
|
75
|
-
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
|
76
57
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
record = @root.min_record
|
82
|
-
while record && page.level > level
|
83
|
-
page = page(record.child_page_number)
|
84
|
-
record = page.min_record
|
85
|
-
end
|
86
|
-
page if page.level == level
|
87
|
-
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
|
88
62
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
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
|
93
66
|
end
|
94
|
-
end
|
95
67
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
record = @root.max_record
|
101
|
-
while record && page.level > level
|
102
|
-
page = page(record.child_page_number)
|
103
|
-
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)
|
104
72
|
end
|
105
|
-
page if page.level == level
|
106
|
-
end
|
107
73
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
112
84
|
end
|
113
|
-
end
|
114
85
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
def field_names
|
121
|
-
record_describer.field_names
|
122
|
-
end
|
123
|
-
|
124
|
-
# Iterate through all file segments in the index.
|
125
|
-
def each_fseg
|
126
|
-
unless block_given?
|
127
|
-
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
|
128
89
|
end
|
129
90
|
|
130
|
-
|
131
|
-
|
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
|
132
101
|
end
|
133
|
-
end
|
134
102
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
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
|
139
106
|
end
|
140
107
|
|
141
|
-
|
142
|
-
|
108
|
+
# Return the file segment with the given name from the fseg header.
|
109
|
+
def fseg(name)
|
110
|
+
@root.fseg_header[name]
|
143
111
|
end
|
144
|
-
end
|
145
112
|
|
146
|
-
|
147
|
-
|
148
|
-
unless block_given?
|
149
|
-
return enum_for(:each_fseg_frag_page, fseg)
|
113
|
+
def field_names
|
114
|
+
record_describer.field_names
|
150
115
|
end
|
151
116
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
117
|
+
# Iterate through all file segments in the index.
|
118
|
+
def each_fseg
|
119
|
+
return enum_for(:each_fseg) unless block_given?
|
156
120
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
return enum_for(:each_page_from, page)
|
121
|
+
FSEG_LIST_NAMES.each do |fseg_name|
|
122
|
+
yield fseg_name, fseg(fseg_name)
|
123
|
+
end
|
161
124
|
end
|
162
125
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
page = page(page.next)
|
167
|
-
end
|
168
|
-
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?
|
169
129
|
|
170
|
-
|
171
|
-
# and following the next pointers in each page.
|
172
|
-
def each_page_at_level(level)
|
173
|
-
unless block_given?
|
174
|
-
return enum_for(:each_page_at_level, level)
|
130
|
+
fseg.each_list(&block)
|
175
131
|
end
|
176
132
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
# Iterate through all records on all leaf pages in ascending order.
|
181
|
-
def each_record
|
182
|
-
unless block_given?
|
183
|
-
return enum_for(:each_record)
|
184
|
-
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?
|
185
136
|
|
186
|
-
|
187
|
-
|
188
|
-
yield record
|
137
|
+
fseg.frag_array_pages.each do |page_number|
|
138
|
+
yield page_number, page(page_number)
|
189
139
|
end
|
190
140
|
end
|
191
|
-
end
|
192
141
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
# record is not found, nil is returned (either because linear_search_in_page
|
197
|
-
# returns nil breaking the loop, or because compare_key returns non-zero).
|
198
|
-
def linear_search(key)
|
199
|
-
Innodb::Stats.increment :linear_search
|
200
|
-
|
201
|
-
page = @root
|
202
|
-
|
203
|
-
if Innodb.debug?
|
204
|
-
puts "linear_search: root=%i, level=%i, key=(%s)" % [
|
205
|
-
page.offset,
|
206
|
-
page.level,
|
207
|
-
key.join(", "),
|
208
|
-
]
|
209
|
-
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?
|
210
145
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
page = page(rec.child_page_number)
|
217
|
-
else
|
218
|
-
# We're on a leaf page, so return the page and record if there is a
|
219
|
-
# match. If there is no match, break the loop and cause nil to be
|
220
|
-
# returned.
|
221
|
-
return rec if rec.compare_key(key) == 0
|
222
|
-
break
|
146
|
+
while page && page.type == :INDEX
|
147
|
+
yield page
|
148
|
+
break unless page.next
|
149
|
+
|
150
|
+
page = page(page.next)
|
223
151
|
end
|
224
152
|
end
|
225
|
-
end
|
226
153
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
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?
|
232
158
|
|
233
|
-
|
234
|
-
|
235
|
-
if Innodb.debug?
|
236
|
-
puts "binary_search: root=%i, level=%i, key=(%s)" % [
|
237
|
-
page.offset,
|
238
|
-
page.level,
|
239
|
-
key.join(", "),
|
240
|
-
]
|
159
|
+
each_page_from(min_page_at_level(level), &block)
|
241
160
|
end
|
242
161
|
|
243
|
-
#
|
244
|
-
|
245
|
-
|
246
|
-
if page.level > 0
|
247
|
-
# If we haven't reached a leaf page yet, move down the tree and search
|
248
|
-
# again using binary search.
|
249
|
-
page = page(rec.child_page_number)
|
250
|
-
else
|
251
|
-
# We're on a leaf page, so return the page and record if there is a
|
252
|
-
# match. If there is no match, break the loop and cause nil to be
|
253
|
-
# returned.
|
254
|
-
return rec if rec.compare_key(key) == 0
|
255
|
-
break
|
256
|
-
end
|
257
|
-
end
|
258
|
-
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?
|
259
165
|
|
260
|
-
|
261
|
-
|
262
|
-
# index.
|
263
|
-
class IndexCursor
|
264
|
-
def initialize(index, record, direction)
|
265
|
-
Innodb::Stats.increment :index_cursor_create
|
266
|
-
@index = index
|
267
|
-
@direction = direction
|
268
|
-
case record
|
269
|
-
when :min
|
270
|
-
# Start at the minimum record on the minimum page in the index.
|
271
|
-
@page = index.min_page_at_level(0)
|
272
|
-
@page_cursor = @page.record_cursor(:min, direction)
|
273
|
-
when :max
|
274
|
-
# Start at the maximum record on the maximum page in the index.
|
275
|
-
@page = index.max_page_at_level(0)
|
276
|
-
@page_cursor = @page.record_cursor(:max, direction)
|
277
|
-
else
|
278
|
-
# Start at the record provided.
|
279
|
-
@page = record.page
|
280
|
-
@page_cursor = @page.record_cursor(record.offset, direction)
|
166
|
+
each_page_at_level(0) do |page|
|
167
|
+
page.each_record(&block)
|
281
168
|
end
|
282
169
|
end
|
283
170
|
|
284
|
-
#
|
285
|
-
|
286
|
-
|
287
|
-
|
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
|
+
]
|
288
187
|
end
|
289
188
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
295
202
|
end
|
296
203
|
end
|
297
204
|
|
298
|
-
#
|
299
|
-
|
300
|
-
|
301
|
-
|
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
|
+
]
|
302
219
|
end
|
303
220
|
|
304
|
-
|
305
|
-
|
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
|
306
236
|
end
|
307
237
|
end
|
308
238
|
|
309
|
-
|
310
|
-
|
311
|
-
#
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
315
261
|
end
|
316
262
|
|
317
|
-
|
318
|
-
|
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
|
319
275
|
end
|
320
|
-
end
|
321
276
|
|
322
|
-
|
323
|
-
|
324
|
-
|
277
|
+
# Iterate through all records in the cursor.
|
278
|
+
def each_record
|
279
|
+
return enum_for(:each_record) unless block_given?
|
325
280
|
|
326
|
-
|
327
|
-
|
281
|
+
while (rec = record)
|
282
|
+
yield rec
|
283
|
+
end
|
328
284
|
end
|
329
285
|
|
330
|
-
|
331
|
-
|
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))
|
332
292
|
end
|
333
293
|
|
334
|
-
|
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
|
335
297
|
|
336
|
-
|
337
|
-
|
298
|
+
if (rec = @page_cursor.record)
|
299
|
+
return rec
|
300
|
+
end
|
338
301
|
|
339
|
-
|
340
|
-
def prev_record
|
341
|
-
Innodb::Stats.increment :index_cursor_prev_record
|
302
|
+
return unless (next_page = @page.next)
|
342
303
|
|
343
|
-
|
344
|
-
return rec
|
345
|
-
end
|
304
|
+
move_cursor(next_page, :min)
|
346
305
|
|
347
|
-
|
348
|
-
return nil
|
306
|
+
@page_cursor.record
|
349
307
|
end
|
350
308
|
|
351
|
-
|
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)
|
352
318
|
|
353
|
-
|
319
|
+
move_cursor(prev_page, :max)
|
320
|
+
|
321
|
+
@page_cursor.record
|
322
|
+
end
|
354
323
|
end
|
355
|
-
end
|
356
324
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
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
|
361
330
|
end
|
362
331
|
end
|