innodb_ruby 0.7.11 → 0.7.12
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.
- data/bin/innodb_space +162 -25
- data/lib/innodb.rb +8 -1
- data/lib/innodb/checksum.rb +30 -0
- data/lib/innodb/cursor.rb +165 -37
- data/lib/innodb/field.rb +31 -57
- data/lib/innodb/field_type.rb +124 -0
- data/lib/innodb/fseg_entry.rb +9 -6
- data/lib/innodb/index.rb +23 -12
- data/lib/innodb/inode.rb +133 -0
- data/lib/innodb/list.rb +44 -12
- data/lib/innodb/log.rb +1 -0
- data/lib/innodb/log_block.rb +2 -1
- data/lib/innodb/page.rb +93 -17
- data/lib/innodb/page/blob.rb +60 -0
- data/lib/innodb/page/fsp_hdr_xdes.rb +34 -24
- data/lib/innodb/page/index.rb +364 -190
- data/lib/innodb/page/inode.rb +20 -51
- data/lib/innodb/page/sys.rb +22 -0
- data/lib/innodb/page/sys_data_dictionary_header.rb +92 -0
- data/lib/innodb/page/sys_rseg_header.rb +59 -0
- data/lib/innodb/page/trx_sys.rb +72 -29
- data/lib/innodb/page/undo_log.rb +95 -0
- data/lib/innodb/record_describer.rb +2 -1
- data/lib/innodb/space.rb +162 -17
- data/lib/innodb/undo_log.rb +73 -0
- data/lib/innodb/version.rb +2 -1
- data/lib/innodb/xdes.rb +44 -11
- metadata +19 -6
data/lib/innodb/space.rb
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
# An InnoDB space file, which can be either a multi-table ibdataN file
|
2
3
|
# or a single-table "innodb_file_per_table" .ibd file.
|
3
4
|
class Innodb::Space
|
4
5
|
# InnoDB's default page size is 16KiB.
|
5
6
|
DEFAULT_PAGE_SIZE = 16384
|
6
7
|
|
7
|
-
#
|
8
|
+
# A map of InnoDB system space fixed-allocation pages. This can be used to
|
9
|
+
# check whether a space is a system space or not, as non-system spaces will
|
10
|
+
# not match this pattern.
|
11
|
+
SYSTEM_SPACE_PAGE_MAP = {
|
12
|
+
0 => :FSP_HDR,
|
13
|
+
1 => :IBUF_BITMAP,
|
14
|
+
2 => :INODE,
|
15
|
+
3 => :SYS,
|
16
|
+
4 => :INDEX,
|
17
|
+
5 => :TRX_SYS,
|
18
|
+
6 => :SYS,
|
19
|
+
7 => :SYS,
|
20
|
+
}
|
21
|
+
|
22
|
+
# Open a space file, optionally providing the page size to use. Pages
|
8
23
|
# that aren't 16 KiB may not be supported well.
|
9
24
|
def initialize(file, page_size=nil)
|
10
25
|
@file = File.open(file)
|
@@ -91,6 +106,24 @@ class Innodb::Space
|
|
91
106
|
(0..(@pages / pages_per_xdes_page)).map { |n| n * pages_per_xdes_page }
|
92
107
|
end
|
93
108
|
|
109
|
+
# The FSP_HDR/XDES page which will contain the XDES entry for a given page.
|
110
|
+
def xdes_page_for_page(page_number)
|
111
|
+
page_number - (page_number % pages_per_xdes_page)
|
112
|
+
end
|
113
|
+
|
114
|
+
# The XDES entry offset for a given page within its FSP_HDR/XDES page's
|
115
|
+
# XDES array.
|
116
|
+
def xdes_entry_for_page(page_number)
|
117
|
+
relative_page_number = page_number - xdes_page_for_page(page_number)
|
118
|
+
relative_page_number / pages_per_extent
|
119
|
+
end
|
120
|
+
|
121
|
+
# Return the Innodb::Xdes entry which represents a given page.
|
122
|
+
def xdes_for_page(page_number)
|
123
|
+
xdes_array = page(xdes_page_for_page(page_number)).each_xdes.to_a
|
124
|
+
xdes_array[xdes_entry_for_page(page_number)]
|
125
|
+
end
|
126
|
+
|
94
127
|
# Get the raw byte buffer of size bytes at offset in the file.
|
95
128
|
def read_at_offset(offset, size)
|
96
129
|
@file.seek(offset)
|
@@ -116,19 +149,56 @@ class Innodb::Space
|
|
116
149
|
this_page
|
117
150
|
end
|
118
151
|
|
152
|
+
# Determine whether this space looks like a system space. If the initial
|
153
|
+
# pages in the space match the SYSTEM_SPACE_PAGE_MAP, it is likely to be
|
154
|
+
# a system space.
|
155
|
+
def system_space?
|
156
|
+
SYSTEM_SPACE_PAGE_MAP.each do |page_number, type|
|
157
|
+
# We can't use page() here, because system_space? need to be used
|
158
|
+
# in the Innodb::Page::Sys.parse to determine what type of page
|
159
|
+
# is being looked at. Using page() would cause us to keep recurse
|
160
|
+
# infinitely. Use Innodb::Page.new instead to load the page as
|
161
|
+
# simply as possible.
|
162
|
+
test_page = Innodb::Page.new(self, page_data(page_number))
|
163
|
+
return false unless test_page.type == type
|
164
|
+
end
|
165
|
+
true
|
166
|
+
end
|
167
|
+
|
119
168
|
# Get (and cache) the FSP header from the FSP_HDR page.
|
120
169
|
def fsp
|
121
170
|
@fsp ||= page(0).fsp_header
|
122
171
|
end
|
123
172
|
|
173
|
+
# Get the Innodb::Page::TrxSys page for a system space.
|
174
|
+
def trx_sys
|
175
|
+
page(5) if system_space?
|
176
|
+
end
|
177
|
+
|
178
|
+
def rseg_page?(page_number)
|
179
|
+
if trx_sys
|
180
|
+
trx_sys.rsegs.include?({
|
181
|
+
:space_id => 0,
|
182
|
+
:page_number => page_number,
|
183
|
+
})
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Get the Innodb::Page::SysDataDictionaryHeader page for a system space.
|
188
|
+
def data_dictionary
|
189
|
+
page(7) if system_space?
|
190
|
+
end
|
191
|
+
|
124
192
|
# Get an Innodb::List object for a specific list by list name.
|
125
193
|
def list(name)
|
126
|
-
|
194
|
+
if xdes_lists.include?(name) || inode_lists.include?(name)
|
195
|
+
fsp[name]
|
196
|
+
end
|
127
197
|
end
|
128
198
|
|
129
199
|
# Get an Innodb::Index object for a specific index by root page number.
|
130
|
-
def index(root_page_number)
|
131
|
-
Innodb::Index.new(self, root_page_number)
|
200
|
+
def index(root_page_number, record_describer=nil)
|
201
|
+
Innodb::Index.new(self, root_page_number, record_describer || @record_describer)
|
132
202
|
end
|
133
203
|
|
134
204
|
# Iterate through each index by guessing that the root pages will be
|
@@ -140,18 +210,55 @@ class Innodb::Space
|
|
140
210
|
return enum_for(:each_index)
|
141
211
|
end
|
142
212
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
213
|
+
if system_space?
|
214
|
+
data_dictionary.each_index do |table_name, index_name, index|
|
215
|
+
yield index
|
216
|
+
end
|
217
|
+
else
|
218
|
+
(3...@pages).each do |page_number|
|
219
|
+
page = page(page_number)
|
220
|
+
if page.type == :INDEX && page.root?
|
221
|
+
yield index(page_number)
|
222
|
+
else
|
223
|
+
break
|
224
|
+
end
|
149
225
|
end
|
150
226
|
end
|
151
227
|
end
|
152
228
|
|
153
|
-
#
|
154
|
-
|
229
|
+
# An array of Innodb::Inode list names.
|
230
|
+
def inode_lists
|
231
|
+
[:full_inodes, :free_inodes]
|
232
|
+
end
|
233
|
+
|
234
|
+
# Iterate through Innodb::Inode lists in the space.
|
235
|
+
def each_inode_list
|
236
|
+
unless block_given?
|
237
|
+
return enum_for(:each_inode_list)
|
238
|
+
end
|
239
|
+
|
240
|
+
inode_lists.each do |name|
|
241
|
+
yield name, list(name)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Iterate through Innodb::Inode objects in the space.
|
246
|
+
def each_inode
|
247
|
+
unless block_given?
|
248
|
+
return enum_for(:each_inode)
|
249
|
+
end
|
250
|
+
|
251
|
+
each_inode_list.each do |name, list|
|
252
|
+
list.each do |page|
|
253
|
+
page.each_inode do |inode|
|
254
|
+
yield inode
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Iterate through all pages in a space, returning the page number and an
|
261
|
+
# Innodb::Page object for each one.
|
155
262
|
def each_page(start_page=0)
|
156
263
|
unless block_given?
|
157
264
|
return enum_for(:each_page, start_page)
|
@@ -163,6 +270,22 @@ class Innodb::Space
|
|
163
270
|
end
|
164
271
|
end
|
165
272
|
|
273
|
+
# An array of Innodb::Xdes list names.
|
274
|
+
def xdes_lists
|
275
|
+
[:free, :free_frag, :full_frag]
|
276
|
+
end
|
277
|
+
|
278
|
+
# Iterate through Innodb::Xdes lists in the space.
|
279
|
+
def each_xdes_list
|
280
|
+
unless block_given?
|
281
|
+
return enum_for(:each_xdes_list)
|
282
|
+
end
|
283
|
+
|
284
|
+
xdes_lists.each do |name|
|
285
|
+
yield name, list(name)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
166
289
|
# Iterate through all FSP_HDR/XDES pages, returning an Innodb::Page object
|
167
290
|
# for each one.
|
168
291
|
def each_xdes_page
|
@@ -192,16 +315,38 @@ class Innodb::Space
|
|
192
315
|
end
|
193
316
|
end
|
194
317
|
|
318
|
+
def each_page_status(start_page=0)
|
319
|
+
unless block_given?
|
320
|
+
return enum_for(:each_page_with_status, start_page)
|
321
|
+
end
|
322
|
+
|
323
|
+
each_xdes do |xdes|
|
324
|
+
xdes.each_page_status do |page_number, page_status|
|
325
|
+
next if page_number < start_page
|
326
|
+
next if page_number >= @pages
|
327
|
+
|
328
|
+
if this_page = page(page_number)
|
329
|
+
yield page_number, this_page, page_status
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def type_for_page(page, page_status)
|
336
|
+
page_status[:free] ? "FREE (#{page.type})" : page.type
|
337
|
+
end
|
338
|
+
|
195
339
|
# Iterate through unique regions in the space by page type. This is useful
|
196
340
|
# to achieve an overall view of the space.
|
197
341
|
def each_page_type_region(start_page=0)
|
198
342
|
unless block_given?
|
199
|
-
return enum_for(:each_page_type_region)
|
343
|
+
return enum_for(:each_page_type_region, start_page)
|
200
344
|
end
|
201
345
|
|
202
346
|
region = nil
|
203
|
-
|
204
|
-
|
347
|
+
each_page_status(start_page) do |page_number, page, page_status|
|
348
|
+
page_type = type_for_page(page, page_status)
|
349
|
+
if region && region[:type] == page_type
|
205
350
|
region[:end] = page_number
|
206
351
|
region[:count] += 1
|
207
352
|
else
|
@@ -209,7 +354,7 @@ class Innodb::Space
|
|
209
354
|
region = {
|
210
355
|
:start => page_number,
|
211
356
|
:end => page_number,
|
212
|
-
:type =>
|
357
|
+
:type => page_type,
|
213
358
|
:count => 1,
|
214
359
|
}
|
215
360
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
class Innodb::UndoLog
|
3
|
+
attr_reader :page
|
4
|
+
attr_reader :position
|
5
|
+
def initialize(page, position)
|
6
|
+
@page = page
|
7
|
+
@position = position
|
8
|
+
end
|
9
|
+
|
10
|
+
def size_xa_header
|
11
|
+
4 + 4 + 4 + 128
|
12
|
+
end
|
13
|
+
|
14
|
+
def size_header
|
15
|
+
8 + 8 + 2 + 2 + 1 + 1 + 8 + 2 + 2 + Innodb::List::NODE_SIZE + size_xa_header
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_header(cursor)
|
19
|
+
cursor.name("header") do |c|
|
20
|
+
xid_exists = nil
|
21
|
+
{
|
22
|
+
:trx_id => c.name("trx_id") { c.get_hex(8) },
|
23
|
+
:trx_no => c.name("trx_no") { c.get_uint64 },
|
24
|
+
:delete_marks => c.name("delete_marks") { (c.get_uint16 != 0) },
|
25
|
+
:log_start => c.name("log_start") { c.get_uint16 },
|
26
|
+
:xid_exists => c.name("xid_exists") { xid_exists = (c.get_uint8 != 0) },
|
27
|
+
:dict_trans => c.name("dict_trans") { (c.get_uint8 != 0) },
|
28
|
+
:table_id => c.name("table_id") { c.get_uint64 },
|
29
|
+
:next_log => c.name("next_log") { c.get_uint16 },
|
30
|
+
:prev_log => c.name("prev_log") { c.get_uint16 },
|
31
|
+
:history_list_node => c.name("history_list_node") {
|
32
|
+
Innodb::List.get_node(c)
|
33
|
+
},
|
34
|
+
:xid => c.name("xid") {
|
35
|
+
if xid_exists
|
36
|
+
{
|
37
|
+
:format => c.name("format") { c.get_uint32 },
|
38
|
+
:trid_len => c.name("trid_len") { c.get_uint32 },
|
39
|
+
:bqual_len => c.name("bqual_len") { c.get_uint32 },
|
40
|
+
:data => c.name("data") { c.get_bytes(128) },
|
41
|
+
}
|
42
|
+
end
|
43
|
+
},
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def undo_log
|
49
|
+
@undo_log ||= page.cursor(position).name("undo_log") do |c|
|
50
|
+
{
|
51
|
+
:header => read_header(c),
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def header
|
57
|
+
undo_log[:header]
|
58
|
+
end
|
59
|
+
|
60
|
+
def prev_address
|
61
|
+
header[:history_list_node][:prev]
|
62
|
+
end
|
63
|
+
|
64
|
+
def next_address
|
65
|
+
header[:history_list_node][:next]
|
66
|
+
end
|
67
|
+
|
68
|
+
def dump
|
69
|
+
puts "header:"
|
70
|
+
pp header
|
71
|
+
puts
|
72
|
+
end
|
73
|
+
end
|
data/lib/innodb/version.rb
CHANGED
data/lib/innodb/xdes.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
# An InnoDB "extent descriptor entry" or "+XDES+". These structures are used
|
2
3
|
# in the +XDES+ entry array contained in +FSP_HDR+ and +XDES+ pages.
|
3
4
|
#
|
@@ -58,14 +59,17 @@ class Innodb::Xdes
|
|
58
59
|
def read_xdes_entry(page, cursor)
|
59
60
|
extent_number = (cursor.position - page.pos_xdes_array) / size_entry
|
60
61
|
start_page = page.offset + (extent_number * page.space.pages_per_extent)
|
61
|
-
{
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
62
|
+
cursor.name("xdes[#{extent_number}]") do |c|
|
63
|
+
{
|
64
|
+
:start_page => start_page,
|
65
|
+
:end_page => start_page + page.space.pages_per_extent - 1,
|
66
|
+
:fseg_id => c.name("fseg_id") { c.get_uint64 },
|
67
|
+
:this => {:page => page.offset, :offset => c.position},
|
68
|
+
:list => c.name("list") { Innodb::List.get_node(c) },
|
69
|
+
:state => c.name("state") { STATES[c.get_uint32] },
|
70
|
+
:bitmap => c.name("bitmap") { c.get_bytes(size_bitmap) },
|
71
|
+
}
|
72
|
+
end
|
69
73
|
end
|
70
74
|
|
71
75
|
# Return the stored extent descriptor entry.
|
@@ -73,6 +77,27 @@ class Innodb::Xdes
|
|
73
77
|
@xdes
|
74
78
|
end
|
75
79
|
|
80
|
+
def start_page; @xdes[:start_page]; end
|
81
|
+
def end_page; @xdes[:end_page]; end
|
82
|
+
def fseg_id; @xdes[:fseg_id]; end
|
83
|
+
def this; @xdes[:this]; end
|
84
|
+
def list; @xdes[:list]; end
|
85
|
+
def state; @xdes[:state]; end
|
86
|
+
def bitmap; @xdes[:bitmap]; end
|
87
|
+
|
88
|
+
# Return whether this XDES entry is allocated to an fseg (the whole extent
|
89
|
+
# then belongs to the fseg).
|
90
|
+
def allocated_to_fseg?
|
91
|
+
fseg_id != 0
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return the status for a given page. This is relatively inefficient as
|
95
|
+
# implemented and could be done better.
|
96
|
+
def page_status(page_number)
|
97
|
+
page_status_array = each_page_status.to_a
|
98
|
+
page_status_array[page_number - xdes[:start_page]][1]
|
99
|
+
end
|
100
|
+
|
76
101
|
# Iterate through all pages represented by this extent descriptor,
|
77
102
|
# yielding a page status hash for each page, containing the following
|
78
103
|
# fields:
|
@@ -93,11 +118,10 @@ class Innodb::Xdes
|
|
93
118
|
page_number = xdes[:start_page] + (byte_index * 4) + page_index
|
94
119
|
page_bits = ((byte >> (page * BITS_PER_PAGE)) & BITMAP_BV_ALL)
|
95
120
|
page_status = {
|
96
|
-
:page => page_number,
|
97
121
|
:free => (page_bits & BITMAP_BV_FREE != 0),
|
98
122
|
:clean => (page_bits & BITMAP_BV_CLEAN != 0),
|
99
123
|
}
|
100
|
-
yield page_status
|
124
|
+
yield page_number, page_status
|
101
125
|
end
|
102
126
|
end
|
103
127
|
|
@@ -106,7 +130,10 @@ class Innodb::Xdes
|
|
106
130
|
|
107
131
|
# Return the count of free pages (free bit is true) on this extent.
|
108
132
|
def free_pages
|
109
|
-
each_page_status.inject(0)
|
133
|
+
each_page_status.inject(0) do |sum, (page_number, page_status)|
|
134
|
+
sum += 1 if page_status[:free]
|
135
|
+
sum
|
136
|
+
end
|
110
137
|
end
|
111
138
|
|
112
139
|
# Return the count of used pages (free bit is false) on this extent.
|
@@ -127,4 +154,10 @@ class Innodb::Xdes
|
|
127
154
|
def next_address
|
128
155
|
xdes[:list][:next]
|
129
156
|
end
|
157
|
+
|
158
|
+
# Compare one Innodb::Xdes to another.
|
159
|
+
def ==(other)
|
160
|
+
xdes[:this][:page] == other.xdes[:this][:page] &&
|
161
|
+
xdes[:this][:offset] == other.xdes[:this][:offset]
|
162
|
+
end
|
130
163
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: innodb_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.12
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bindata
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,12 @@ dependencies:
|
|
21
21
|
version: 1.4.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.4.5
|
25
30
|
description: Library for parsing InnoDB data files in Ruby
|
26
31
|
email: jeremy@jcole.us
|
27
32
|
executables:
|
@@ -32,20 +37,29 @@ extra_rdoc_files: []
|
|
32
37
|
files:
|
33
38
|
- README.md
|
34
39
|
- lib/innodb.rb
|
40
|
+
- lib/innodb/checksum.rb
|
35
41
|
- lib/innodb/cursor.rb
|
36
42
|
- lib/innodb/field.rb
|
43
|
+
- lib/innodb/field_type.rb
|
37
44
|
- lib/innodb/fseg_entry.rb
|
38
45
|
- lib/innodb/index.rb
|
46
|
+
- lib/innodb/inode.rb
|
39
47
|
- lib/innodb/list.rb
|
40
48
|
- lib/innodb/log.rb
|
41
49
|
- lib/innodb/log_block.rb
|
42
50
|
- lib/innodb/page.rb
|
51
|
+
- lib/innodb/page/blob.rb
|
43
52
|
- lib/innodb/page/fsp_hdr_xdes.rb
|
44
53
|
- lib/innodb/page/index.rb
|
45
54
|
- lib/innodb/page/inode.rb
|
55
|
+
- lib/innodb/page/sys.rb
|
56
|
+
- lib/innodb/page/sys_data_dictionary_header.rb
|
57
|
+
- lib/innodb/page/sys_rseg_header.rb
|
46
58
|
- lib/innodb/page/trx_sys.rb
|
59
|
+
- lib/innodb/page/undo_log.rb
|
47
60
|
- lib/innodb/record_describer.rb
|
48
61
|
- lib/innodb/space.rb
|
62
|
+
- lib/innodb/undo_log.rb
|
49
63
|
- lib/innodb/version.rb
|
50
64
|
- lib/innodb/xdes.rb
|
51
65
|
- bin/innodb_log
|
@@ -70,9 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
84
|
version: '0'
|
71
85
|
requirements: []
|
72
86
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.8.
|
87
|
+
rubygems_version: 1.8.23
|
74
88
|
signing_key:
|
75
89
|
specification_version: 3
|
76
90
|
summary: InnoDB data file parser
|
77
91
|
test_files: []
|
78
|
-
has_rdoc:
|