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