innodb_ruby 0.9.16 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|