innodb_ruby 0.6.4 → 0.6.6

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 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