innodb_ruby 0.9.16 → 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 +5 -5
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +377 -757
- data/lib/innodb.rb +4 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -326
- 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 +154 -155
- 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 +417 -414
- 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 +964 -943
- 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 -160
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +380 -418
- 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 +80 -11
data/lib/innodb/inode.rb
CHANGED
@@ -1,187 +1,186 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
58
|
-
|
59
|
-
attr_accessor :space
|
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
|
60
68
|
|
61
|
-
|
62
|
-
|
63
|
-
@data = data
|
64
|
-
end
|
69
|
+
attr_accessor :space
|
70
|
+
attr_accessor :header
|
65
71
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
72
|
+
def initialize(space, header)
|
73
|
+
@space = space
|
74
|
+
@header = header
|
75
|
+
end
|
82
76
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
88
93
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
98
104
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
105
|
+
# Helper method to count non-nil fragment pages.
|
106
|
+
def frag_array_n_used
|
107
|
+
frag_array_pages.count
|
108
|
+
end
|
104
109
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
(full.length * @space.pages_per_extent)
|
111
|
-
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
|
112
115
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
117
123
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
122
128
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
end
|
129
|
+
# Return a list from the fseg, given its name as a symbol.
|
130
|
+
def list(name)
|
131
|
+
return unless LISTS.include?(name)
|
127
132
|
|
128
|
-
|
129
|
-
def each_list
|
130
|
-
unless block_given?
|
131
|
-
return enum_for(:each_list)
|
133
|
+
header[name]
|
132
134
|
end
|
133
135
|
|
134
|
-
lists
|
135
|
-
|
136
|
-
|
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?
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
+
LISTS.each do |name|
|
141
|
+
yield name, list(name)
|
142
|
+
end
|
140
143
|
|
141
|
-
|
142
|
-
# page number. This allows a convenient way to identify all pages that are
|
143
|
-
# part of this inode.
|
144
|
-
def each_page_number
|
145
|
-
unless block_given?
|
146
|
-
return enum_for(:each_page_number)
|
144
|
+
nil
|
147
145
|
end
|
148
146
|
|
149
|
-
|
150
|
-
|
151
|
-
|
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)
|
152
154
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
yield page_number
|
155
|
+
each_list do |_fseg_name, fseg_list|
|
156
|
+
fseg_list.each do |xdes|
|
157
|
+
xdes.each_page_status(&block)
|
157
158
|
end
|
158
159
|
end
|
160
|
+
|
161
|
+
nil
|
159
162
|
end
|
160
163
|
|
161
|
-
|
162
|
-
|
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?
|
163
168
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
unless block_given?
|
168
|
-
return enum_for(:each_page)
|
169
|
-
end
|
169
|
+
each_page_number do |page_number|
|
170
|
+
yield page_number, space.page(page_number)
|
171
|
+
end
|
170
172
|
|
171
|
-
|
172
|
-
yield page_number, space.page(page_number)
|
173
|
+
nil
|
173
174
|
end
|
174
175
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
def ==(other)
|
180
|
-
fseg_id == other.fseg_id if other
|
181
|
-
end
|
176
|
+
# Compare one Innodb::Inode to another.
|
177
|
+
def ==(other)
|
178
|
+
fseg_id == other.fseg_id if other
|
179
|
+
end
|
182
180
|
|
183
|
-
|
184
|
-
|
185
|
-
|
181
|
+
# Dump a summary of this object for debugging purposes.
|
182
|
+
def dump
|
183
|
+
pp header
|
184
|
+
end
|
186
185
|
end
|
187
186
|
end
|
data/lib/innodb/list.rb
CHANGED
@@ -1,233 +1,241 @@
|
|
1
|
-
#
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
68
|
+
attr_reader :space
|
69
|
+
attr_reader :base
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
71
|
+
def initialize(space, base)
|
72
|
+
@space = space
|
73
|
+
@base = base
|
74
|
+
end
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
82
|
-
|
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
|
-
|
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
|
-
|
92
|
-
|
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
|
-
|
95
|
-
|
96
|
-
@base[:length]
|
97
|
-
end
|
95
|
+
object_from_address(object.next_address)
|
96
|
+
end
|
98
97
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
98
|
+
# Return the number of items in the list.
|
99
|
+
def length
|
100
|
+
@base.length
|
101
|
+
end
|
104
102
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
103
|
+
# Return the first object in the list using the list base node "first"
|
104
|
+
# address pointer.
|
105
|
+
def first
|
106
|
+
object_from_address(@base.first)
|
107
|
+
end
|
110
108
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
# Return the first object in the list using the list base node "last"
|
110
|
+
# address pointer.
|
111
|
+
def last
|
112
|
+
object_from_address(@base.last)
|
113
|
+
end
|
115
114
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
def include?(item)
|
121
|
-
each.to_a.include? item
|
122
|
-
end
|
115
|
+
# Return a list cursor for the list.
|
116
|
+
def list_cursor(node = :min, direction = :forward)
|
117
|
+
ListCursor.new(self, node, direction)
|
118
|
+
end
|
123
119
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
120
|
+
# Return whether the given item is present in the list. This depends on the
|
121
|
+
# item and the items in the list implementing some sufficient == method.
|
122
|
+
# This is implemented rather inefficiently by constructing an array and
|
123
|
+
# leaning on Array#include? to do the real work.
|
124
|
+
def include?(item)
|
125
|
+
each.to_a.include?(item)
|
128
126
|
end
|
129
127
|
|
130
|
-
|
131
|
-
|
128
|
+
# Iterate through all nodes in the list.
|
129
|
+
def each(&block)
|
130
|
+
return enum_for(:each) unless block_given?
|
131
|
+
|
132
|
+
list_cursor.each_node(&block)
|
132
133
|
end
|
133
|
-
end
|
134
134
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
case node
|
144
|
-
when :min
|
145
|
-
@node = @list.first
|
146
|
-
when :max
|
147
|
-
@node = @list.last
|
148
|
-
else
|
149
|
-
@node = node
|
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
|
+
@node = initial_node(node)
|
150
143
|
end
|
151
|
-
end
|
152
144
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
145
|
+
def initial_node(node)
|
146
|
+
case node
|
147
|
+
when :min
|
148
|
+
@list.first
|
149
|
+
when :max
|
150
|
+
@list.last
|
151
|
+
else
|
152
|
+
node
|
153
|
+
end
|
157
154
|
end
|
158
155
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
156
|
+
def node
|
157
|
+
if @initial
|
158
|
+
@initial = false
|
159
|
+
return @node
|
160
|
+
end
|
161
|
+
|
162
|
+
case @direction
|
163
|
+
when :forward
|
164
|
+
next_node
|
165
|
+
when :backward
|
166
|
+
prev_node
|
167
|
+
end
|
164
168
|
end
|
165
|
-
end
|
166
169
|
|
167
|
-
|
168
|
-
|
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
|
170
|
+
def goto_node(node)
|
171
|
+
@node = node if node
|
174
172
|
end
|
175
|
-
end
|
176
173
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
@node = node
|
174
|
+
# Return the previous entry from the current position, and advance the
|
175
|
+
# cursor position to the returned entry. If the cursor is currently nil,
|
176
|
+
# return the last entry in the list and adjust the cursor position to
|
177
|
+
# that entry.
|
178
|
+
def prev_node
|
179
|
+
goto_node(@list.prev(@node))
|
184
180
|
end
|
185
|
-
end
|
186
181
|
|
187
|
-
|
188
|
-
|
189
|
-
|
182
|
+
# Return the next entry from the current position, and advance the
|
183
|
+
# cursor position to the returned entry. If the cursor is currently nil,
|
184
|
+
# return the first entry in the list and adjust the cursor position to
|
185
|
+
# that entry.
|
186
|
+
def next_node
|
187
|
+
goto_node(@list.next(@node))
|
190
188
|
end
|
191
189
|
|
192
|
-
|
193
|
-
|
190
|
+
def each_node
|
191
|
+
return enum_for(:each_node) unless block_given?
|
192
|
+
|
193
|
+
while (n = node)
|
194
|
+
yield n
|
195
|
+
end
|
194
196
|
end
|
195
197
|
end
|
196
|
-
end
|
197
|
-
end
|
198
198
|
|
199
|
-
# A list of extent descriptor entries. Objects returned by list methods
|
200
|
-
# will be Innodb::Xdes objects.
|
201
|
-
class
|
202
|
-
|
203
|
-
|
204
|
-
|
199
|
+
# A list of extent descriptor entries. Objects returned by list methods
|
200
|
+
# will be Innodb::Xdes objects.
|
201
|
+
class Xdes < Innodb::List
|
202
|
+
def object_from_address(address)
|
203
|
+
return unless address
|
204
|
+
|
205
|
+
page = @space.page(address.page)
|
206
|
+
return unless page
|
207
|
+
|
208
|
+
Innodb::Xdes.new(page, page.cursor(address.offset - 8))
|
209
|
+
end
|
205
210
|
end
|
206
|
-
end
|
207
|
-
end
|
208
211
|
|
209
|
-
# A list of Inode pages. Objects returned by list methods will be
|
210
|
-
# Innodb::Page::Inode objects.
|
211
|
-
class
|
212
|
-
|
213
|
-
|
214
|
-
|
212
|
+
# A list of Inode pages. Objects returned by list methods will be
|
213
|
+
# Innodb::Page::Inode objects.
|
214
|
+
class Inode < Innodb::List
|
215
|
+
def object_from_address(address)
|
216
|
+
return unless address
|
217
|
+
|
218
|
+
@space.page(address.page)
|
219
|
+
end
|
215
220
|
end
|
216
|
-
end
|
217
|
-
end
|
218
221
|
|
219
|
-
class
|
220
|
-
|
221
|
-
|
222
|
-
|
222
|
+
class UndoPage < Innodb::List
|
223
|
+
def object_from_address(address)
|
224
|
+
return unless address
|
225
|
+
|
226
|
+
@space.page(address.page)
|
227
|
+
end
|
223
228
|
end
|
224
|
-
end
|
225
|
-
end
|
226
229
|
|
227
|
-
class
|
228
|
-
|
229
|
-
|
230
|
-
|
230
|
+
class History < Innodb::List
|
231
|
+
def object_from_address(address)
|
232
|
+
return unless address
|
233
|
+
|
234
|
+
page = @space.page(address.page)
|
235
|
+
return unless page
|
236
|
+
|
237
|
+
Innodb::UndoLog.new(page, address.offset - 34)
|
238
|
+
end
|
231
239
|
end
|
232
240
|
end
|
233
241
|
end
|