innodb_ruby 0.6.4 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
data/bin/innodb_space CHANGED
@@ -40,11 +40,11 @@ def space_index_pages_summary(space)
40
40
  when :INDEX
41
41
  puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [
42
42
  page_number,
43
- page.ph[:index_id],
44
- page.ph[:level],
43
+ page.page_header[:index_id],
44
+ page.level,
45
45
  page.record_space,
46
46
  page.free_space,
47
- page.ph[:n_recs],
47
+ page.records,
48
48
  ]
49
49
  when :ALLOCATED
50
50
  puts "%-12i%-8i%-8i%-8i%-8i%-8i" % [ page_number, 0, 0, 0, page.size, 0 ]
@@ -119,11 +119,11 @@ end
119
119
  def index_recurse(index)
120
120
  index.recurse(
121
121
  lambda do |page, depth|
122
- puts "%s%s %i: %i records, %i bytes" % [
122
+ puts "%s%s NODE #%i: %i records, %i bytes" % [
123
123
  " " * depth,
124
- page.level == 0 ? "LEAF" : "NODE",
124
+ index.node_type(page).to_s.upcase,
125
125
  page.offset,
126
- page.page_header[:n_recs],
126
+ page.records,
127
127
  page.record_space,
128
128
  ]
129
129
  if page.level == 0
@@ -137,12 +137,10 @@ def index_recurse(index)
137
137
  end
138
138
  end,
139
139
  lambda do |parent_page, child_page, child_min_key, depth|
140
- puts "%sLINK %i -> %i: %s records >= (%s)" % [
140
+ puts "%sNODE POINTER RECORD >= (%s) -> #%i" % [
141
141
  " " * depth,
142
- parent_page.offset,
143
- child_page.offset,
144
- child_page.page_header[:n_recs],
145
142
  child_min_key.join(", "),
143
+ child_page.offset,
146
144
  ]
147
145
  end
148
146
  )
@@ -156,7 +154,7 @@ def index_digraph(index)
156
154
  lambda do |page, depth|
157
155
  label = "<page>Page %i|(%i records)" % [
158
156
  page.offset,
159
- page.page_header[:n_recs],
157
+ page.records,
160
158
  ]
161
159
  page.each_child_page do |child_page_number, child_key|
162
160
  label += "|<dir_%i>(%s)" % [
@@ -197,11 +195,11 @@ def index_level_summary(index, levels)
197
195
  index.each_page_at_level(level) do |page|
198
196
  puts "%-8i%-8i%-8i%-8i%-8i%-8i%s" % [
199
197
  page.offset,
200
- page.ph[:index_id],
201
- page.ph[:level],
198
+ page.page_header[:index_id],
199
+ page.level,
202
200
  page.record_space,
203
201
  page.free_space,
204
- page.ph[:n_recs],
202
+ page.records,
205
203
  page.first_record[:key].join("|"),
206
204
  ]
207
205
  end
data/lib/innodb.rb CHANGED
@@ -6,6 +6,7 @@ require "innodb/page"
6
6
  require "innodb/page/fsp_hdr_xdes"
7
7
  require "innodb/page/inode"
8
8
  require "innodb/page/index"
9
+ require "innodb/page/trx_sys"
9
10
  require "innodb/record_describer"
10
11
  require "innodb/space"
11
12
  require "innodb/index"
@@ -0,0 +1,11 @@
1
+ class Innodb::FsegEntry
2
+ SIZE = 4 + 4 + 2
3
+
4
+ def self.get_entry(cursor)
5
+ {
6
+ :space_id => cursor.get_uint32,
7
+ :page_number => cursor.get_uint32,
8
+ :offset => cursor.get_uint16,
9
+ }
10
+ end
11
+ end
data/lib/innodb/index.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # An InnoDB index B-tree, given an Innodb::Space and a root page number.
2
2
  class Innodb::Index
3
+ attr_reader :root
4
+
3
5
  def initialize(space, root_page_number)
4
6
  @space = space
5
7
  @root = @space.page(root_page_number)
@@ -13,11 +15,6 @@ class Innodb::Index
13
15
  raise "Page #{root_page_number} is a #{@root.type} page, not an INDEX page"
14
16
  end
15
17
 
16
- # The root page should not be a leaf page.
17
- unless @root.level > 0
18
- raise "Page #{root_page_number} is a leaf page"
19
- end
20
-
21
18
  # The root page should be the only page at its level.
22
19
  unless @root.prev.nil? && @root.next.nil?
23
20
  raise "Page #{root_page_number} is a node page, but not appear to be the root; it has previous page and next page pointers"
@@ -29,6 +26,17 @@ class Innodb::Index
29
26
  @root.page_header[:index_id]
30
27
  end
31
28
 
29
+ # Return the type of node that the given page represents in the index tree.
30
+ def node_type(page)
31
+ if @root.offset == page.offset
32
+ :root
33
+ elsif page.level == 0
34
+ :leaf
35
+ else
36
+ :internal
37
+ end
38
+ end
39
+
32
40
  # Internal method used by recurse.
33
41
  def _recurse(parent_page, page_proc, link_proc, depth=0)
34
42
  if page_proc && parent_page.type == :INDEX
@@ -87,4 +95,56 @@ class Innodb::Index
87
95
  end
88
96
  end
89
97
  end
90
- end
98
+
99
+ # Compare two arrays of fields to determine if they are equal. This follows
100
+ # the same comparison rules as strcmp and others:
101
+ # 0 = a is equal to b
102
+ # -1 = a is less than b
103
+ # +1 = a is greater than b
104
+ def compare_key(a, b)
105
+ return -1 if a.size < b.size
106
+ return +1 if a.size > b.size
107
+ a.each_index do |i|
108
+ return -1 if a[i] < b[i]
109
+ return +1 if a[i] > b[i]
110
+ end
111
+ return 0
112
+ end
113
+
114
+ # Search for a record within a single page, and return either a perfect
115
+ # match for the key, or the last record closest to they key but not greater
116
+ # than the key. (If an exact match is desired, compare_key must be used to
117
+ # check if the returned record matches. This makes the function useful for
118
+ # search in both leaf and non-leaf pages.)
119
+ def linear_search_in_page(page, key)
120
+ c = page.record_cursor(page.infimum[:next])
121
+ this_rec = c.record
122
+ while next_rec = c.record
123
+ return this_rec if next_rec == page.supremum
124
+ if (compare_key(key, this_rec[:key]) >= 0) &&
125
+ (compare_key(key, next_rec[:key]) < 0)
126
+ return this_rec
127
+ end
128
+ this_rec = next_rec
129
+ end
130
+ this_rec
131
+ end
132
+
133
+ # Search for a record within the entire index, walking down the non-leaf
134
+ # pages until a leaf page is found, and then verifying that the record
135
+ # returned on the leaf page is an exact match for the key. If a matching
136
+ # record is not found, nil is returned (either because linear_search_in_page
137
+ # returns nil breaking the loop, or because compare_key returns non-zero).
138
+ def linear_search(key)
139
+ page = @root
140
+
141
+ while rec = linear_search_in_page(page, key)
142
+ if page.level > 0
143
+ page = @space.page(rec[:child_page_number])
144
+ else
145
+ return page, rec if compare_key(key, rec[:key]) == 0
146
+ break
147
+ end
148
+ end
149
+ end
150
+ end
data/lib/innodb/page.rb CHANGED
@@ -98,7 +98,6 @@ class Innodb::Page
98
98
  :space_id => c.get_uint32,
99
99
  }
100
100
  end
101
- alias :fh :fil_header
102
101
 
103
102
  # A helper function to return the page type from the "fil" header, for easier
104
103
  # access.
@@ -1,3 +1,5 @@
1
+ require "innodb/fseg_entry"
2
+
1
3
  class Innodb::Page::Index < Innodb::Page
2
4
  attr_accessor :record_describer
3
5
 
@@ -20,7 +22,7 @@ class Innodb::Page::Index < Innodb::Page
20
22
 
21
23
  # The size of the "fseg" header.
22
24
  def size_fseg_header
23
- 2 * 10
25
+ 2 * Innodb::FsegEntry::SIZE
24
26
  end
25
27
 
26
28
  # Return the byte offset of the start of records within the page (the
@@ -69,9 +71,14 @@ class Innodb::Page::Index < Innodb::Page
69
71
  pos_user_records
70
72
  end
71
73
 
74
+ # The number of directory slots in use.
75
+ def directory_slots
76
+ page_header[:n_dir_slots]
77
+ end
78
+
72
79
  # The amount of space consumed by the page directory.
73
80
  def directory_space
74
- page_header[:n_dir_slots] * PAGE_DIR_SLOT_SIZE
81
+ directory_slots * PAGE_DIR_SLOT_SIZE
75
82
  end
76
83
 
77
84
  # The amount of space consumed by the trailers in the page.
@@ -129,7 +136,6 @@ class Innodb::Page::Index < Innodb::Page
129
136
  :format => (n_heap & 1<<15) == 0 ? :redundant : :compact,
130
137
  }
131
138
  end
132
- alias :ph :page_header
133
139
 
134
140
  # A helper function to return the page level from the "page" header, for
135
141
  # easier access.
@@ -137,27 +143,23 @@ class Innodb::Page::Index < Innodb::Page
137
143
  page_header && page_header[:level]
138
144
  end
139
145
 
146
+ # A helper function to return the number of records.
147
+ def records
148
+ page_header && page_header[:n_recs]
149
+ end
150
+
140
151
  # A helper function to identify root index pages; they must be the only pages
141
152
  # at their level.
142
153
  def root?
143
154
  self.prev.nil? && self.next.nil?
144
155
  end
145
156
 
146
- # Parse an "fseg" header entry.
147
- def fseg_entry(cursor)
148
- {
149
- :space_id => cursor.get_uint32,
150
- :page_number => cursor.get_uint32,
151
- :offset => cursor.get_uint16,
152
- }
153
- end
154
-
155
157
  # Return the "fseg" header.
156
158
  def fseg_header
157
159
  c = cursor(pos_fseg_header)
158
160
  @fseg_header ||= {
159
- :free_list => fseg_entry(c),
160
- :btree_segment => fseg_entry(c),
161
+ :free_list => Innodb::FsegEntry.get_entry(c),
162
+ :btree_segment => Innodb::FsegEntry.get_entry(c),
161
163
  }
162
164
  end
163
165
 
@@ -260,8 +262,8 @@ class Innodb::Page::Index < Innodb::Page
260
262
  def record(offset)
261
263
  return nil unless offset
262
264
  return nil unless type == :INDEX
263
- return nil if offset == pos_infimum
264
- return nil if offset == pos_supremum
265
+ return infimum if offset == pos_infimum
266
+ return supremum if offset == pos_supremum
265
267
 
266
268
  c = cursor(offset).forward
267
269
 
@@ -269,6 +271,7 @@ class Innodb::Page::Index < Innodb::Page
269
271
  header = record_header(offset)
270
272
 
271
273
  this_record = {
274
+ :offset => offset,
272
275
  :header => header,
273
276
  :next => header[:next] == 0 ? nil : (offset + header[:next]),
274
277
  }
@@ -311,17 +314,48 @@ class Innodb::Page::Index < Innodb::Page
311
314
  this_record
312
315
  end
313
316
 
317
+ # A class for cursoring through records starting from an arbitrary point.
318
+ class RecordCursor
319
+ def initialize(page, offset)
320
+ @page = page
321
+ @offset = offset
322
+ end
323
+
324
+ # Return the next record, and advance the cursor. Return nil when the
325
+ # end of records is reached.
326
+ def record
327
+ return nil unless @offset
328
+
329
+ record = @page.record(@offset)
330
+
331
+ if record == @page.supremum
332
+ @offset = nil
333
+ else
334
+ @offset = record[:next]
335
+ record
336
+ end
337
+ end
338
+ end
339
+
340
+ # Return a RecordCursor starting at offset.
341
+ def record_cursor(offset)
342
+ RecordCursor.new(self, offset)
343
+ end
344
+
314
345
  # Return the first record on this page.
315
346
  def first_record
316
- record(infimum[:next])
347
+ first = record(infimum[:next])
348
+ first if first != supremum
317
349
  end
318
350
 
319
- # Iterate through all records. (This is mostly unimplemented.)
351
+ # Iterate through all records.
320
352
  def each_record
321
- rec = infimum
322
- while rec = record(rec[:next])
353
+ c = record_cursor(infimum[:next])
354
+
355
+ while rec = c.record
323
356
  yield rec
324
357
  end
358
+
325
359
  nil
326
360
  end
327
361
 
@@ -330,12 +364,27 @@ class Innodb::Page::Index < Innodb::Page
330
364
  # record.
331
365
  def each_child_page
332
366
  return nil if level == 0
367
+
333
368
  each_record do |rec|
334
369
  yield rec[:child_page_number], rec[:key]
335
370
  end
371
+
336
372
  nil
337
373
  end
338
374
 
375
+ # Return an array of row offsets for all entries in the page directory.
376
+ def directory
377
+ return @directory if @directory
378
+
379
+ @directory = []
380
+ c = cursor(pos_directory).backward
381
+ directory_slots.times do
382
+ @directory.push c.get_uint16
383
+ end
384
+
385
+ @directory
386
+ end
387
+
339
388
  # Dump the contents of a page for debugging purposes.
340
389
  def dump
341
390
  super
@@ -366,6 +415,10 @@ class Innodb::Page::Index < Innodb::Page
366
415
  pp supremum
367
416
  puts
368
417
 
418
+ puts "page directory:"
419
+ pp directory
420
+ puts
421
+
369
422
  puts "records:"
370
423
  each_record do |rec|
371
424
  pp rec
@@ -0,0 +1,76 @@
1
+ class Innodb::Page::TrxSys < Innodb::Page
2
+ def pos_trx_sys_header
3
+ pos_fil_header + size_fil_header
4
+ end
5
+
6
+ def pos_mysql_binary_log_info
7
+ size - 1000
8
+ end
9
+
10
+ def pos_mysql_master_log_info
11
+ size - 2000
12
+ end
13
+
14
+ def pos_doublewrite_info
15
+ size - 200
16
+ end
17
+
18
+ MYSQL_LOG_MAGIC_N = 873422344
19
+
20
+ def mysql_log_info(offset)
21
+ c = cursor(offset)
22
+ if c.get_uint32 == MYSQL_LOG_MAGIC_N
23
+ {
24
+ :offset => c.get_uint64,
25
+ :name => c.get_bytes(100),
26
+ }
27
+ end
28
+ end
29
+
30
+ DOUBLEWRITE_MAGIC_N = 536853855
31
+
32
+ def doublewrite_page_info(cursor)
33
+ {
34
+ :magic_n => cursor.get_uint32,
35
+ :page_number => [
36
+ cursor.get_uint32,
37
+ cursor.get_uint32,
38
+ ],
39
+ }
40
+ end
41
+
42
+ DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N = 1783657386
43
+
44
+ def doublewrite_info
45
+ c = cursor(pos_doublewrite_info)
46
+ {
47
+ :fseg => Innodb::FsegEntry.get_entry(c),
48
+ :page_info => [
49
+ doublewrite_page_info(c),
50
+ doublewrite_page_info(c),
51
+ ],
52
+ :space_id_stored => c.get_uint32 == DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N,
53
+ }
54
+ end
55
+
56
+ def trx_sys
57
+ c = cursor(pos_trx_sys_header)
58
+ @trx_sys ||= {
59
+ :trx_id => c.get_uint64,
60
+ :fseg => Innodb::FsegEntry.get_entry(c),
61
+ :binary_log => mysql_log_info(pos_mysql_binary_log_info),
62
+ :master_log => mysql_log_info(pos_mysql_master_log_info),
63
+ :doublewrite => doublewrite_info,
64
+ }
65
+ end
66
+
67
+ def dump
68
+ super
69
+
70
+ puts "trx_sys:"
71
+ pp trx_sys
72
+ puts
73
+ end
74
+ end
75
+
76
+ Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
@@ -1,3 +1,3 @@
1
1
  module Innodb
2
- VERSION = "0.6.4"
2
+ VERSION = "0.6.6"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: innodb_ruby
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 6
9
- - 4
10
- version: 0.6.4
9
+ - 6
10
+ version: 0.6.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jeremy Cole
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-12-05 00:00:00 Z
18
+ date: 2012-12-07 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: Library for parsing InnoDB data files in Ruby
@@ -31,6 +31,7 @@ files:
31
31
  - lib/innodb.rb
32
32
  - lib/innodb/cursor.rb
33
33
  - lib/innodb/free_list.rb
34
+ - lib/innodb/fseg_entry.rb
34
35
  - lib/innodb/index.rb
35
36
  - lib/innodb/log.rb
36
37
  - lib/innodb/log_block.rb
@@ -38,6 +39,7 @@ files:
38
39
  - lib/innodb/page/fsp_hdr_xdes.rb
39
40
  - lib/innodb/page/index.rb
40
41
  - lib/innodb/page/inode.rb
42
+ - lib/innodb/page/trx_sys.rb
41
43
  - lib/innodb/record_describer.rb
42
44
  - lib/innodb/space.rb
43
45
  - lib/innodb/version.rb