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.
@@ -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: