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.
@@ -1,2 +1,3 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  # An empty class in order to establish the Innodb::RecordDescriber namespace.
2
- class Innodb::RecordDescriber; end
3
+ class Innodb::RecordDescriber; end
@@ -1,10 +1,25 @@
1
- # An InnoDB tablespace file, which can be either a multi-table ibdataN file
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
- # Open a tablespace file, optionally providing the page size to use. Pages
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
- fsp[name]
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
- (3...@pages).each do |page_number|
144
- page = page(page_number)
145
- if page.type == :INDEX && page.root?
146
- yield index(page_number)
147
- else
148
- break
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
- # Iterate through all pages in a tablespace, returning the page number
154
- # and an Innodb::Page object for each one.
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
- each_page(start_page) do |page_number, page|
204
- if region && region[:type] == page.type
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 => page.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
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Innodb
2
- VERSION = "0.7.11"
3
+ VERSION = "0.7.12"
3
4
  end
@@ -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
- :start_page => start_page,
63
- :fseg_id => cursor.get_uint64,
64
- :this => {:page => page.offset, :offset => cursor.position},
65
- :list => Innodb::List.get_node(cursor),
66
- :state => STATES[cursor.get_uint32],
67
- :bitmap => cursor.get_bytes(size_bitmap),
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) { |sum, p| sum += 1 if p[:free]; sum }
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.11
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-01-15 00:00:00.000000000 Z
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: &70308829886920 !ruby/object:Gem::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: *70308829886920
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.6
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: