innodb_ruby 0.9.0 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,6 +28,10 @@ class Innodb::Page::Inode < Innodb::Page
28
28
  (size - pos_inode_array - 10) / Innodb::Inode::SIZE
29
29
  end
30
30
 
31
+ def size_inode_array
32
+ inodes_per_page * Innodb::Inode::SIZE
33
+ end
34
+
31
35
  # Return the list entry.
32
36
  def list_entry
33
37
  cursor(pos_list_entry).name("list") do |c|
@@ -50,7 +54,7 @@ class Innodb::Page::Inode < Innodb::Page
50
54
  # Read a single Inode entry from the provided byte offset by creating a
51
55
  # cursor and reading the inode using the inode method.
52
56
  def inode_at(cursor)
53
- cursor.name("inode") { |c| Innodb::Inode.new_from_cursor(@space, c) }
57
+ cursor.name("inode[#{cursor.position}]") { |c| Innodb::Inode.new_from_cursor(@space, c) }
54
58
  end
55
59
 
56
60
  # Iterate through all Inodes in the inode array.
@@ -68,6 +72,34 @@ class Innodb::Page::Inode < Innodb::Page
68
72
  end
69
73
  end
70
74
 
75
+ def each_region
76
+ unless block_given?
77
+ return enum_for(:each_region)
78
+ end
79
+
80
+ super do |region|
81
+ yield region
82
+ end
83
+
84
+ yield({
85
+ :offset => pos_list_entry,
86
+ :length => size_list_entry,
87
+ :name => :list_entry,
88
+ :info => "Inode List Entry",
89
+ })
90
+
91
+ each_inode do |inode|
92
+ yield({
93
+ :offset => inode.offset,
94
+ :length => Innodb::Inode::SIZE,
95
+ :name => :inode,
96
+ :info => "Inode",
97
+ })
98
+ end
99
+
100
+ nil
101
+ end
102
+
71
103
  # Dump the contents of a page for debugging purposes.
72
104
  def dump
73
105
  super
@@ -41,6 +41,25 @@ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
41
41
  end
42
42
  end
43
43
 
44
+ def each_region
45
+ unless block_given?
46
+ return enum_for(:each_region)
47
+ end
48
+
49
+ super do |region|
50
+ yield region
51
+ end
52
+
53
+ yield({
54
+ :offset => pos_data_dictionary_header,
55
+ :length => size_data_dictionary_header,
56
+ :name => :data_dictionary_header,
57
+ :info => "Data Dictionary Header",
58
+ })
59
+
60
+ nil
61
+ end
62
+
44
63
  def dump
45
64
  super
46
65
 
@@ -1,6 +1,9 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  class Innodb::Page::SysRsegHeader < Innodb::Page
4
+ # The number of undo log slots in the page.
5
+ UNDO_SEGMENT_SLOTS = 1024
6
+
4
7
  # The position of the rollback segment header within the page.
5
8
  def pos_rseg_header
6
9
  pos_fil_header + size_fil_header
@@ -15,6 +18,10 @@ class Innodb::Page::SysRsegHeader < Innodb::Page
15
18
  pos_rseg_header + size_rseg_header
16
19
  end
17
20
 
21
+ def size_undo_segment_slot
22
+ 4
23
+ end
24
+
18
25
  # Parse the rollback segment header from the page.
19
26
  def rseg_header
20
27
  cursor(pos_rseg_header).name("rseg_header") do |c|
@@ -29,21 +36,53 @@ class Innodb::Page::SysRsegHeader < Innodb::Page
29
36
  end
30
37
  end
31
38
 
39
+ def history_list
40
+ Innodb::HistoryList.new(rseg_header[:history_list])
41
+ end
42
+
32
43
  def each_undo_segment
33
44
  unless block_given?
34
45
  return enum_for(:each_undo_segment)
35
46
  end
36
47
 
37
48
  cursor(pos_undo_segment_array).name("undo_segment_array") do |c|
38
- (0...1023).each do |slot|
49
+ (0...UNDO_SEGMENT_SLOTS).each do |slot|
39
50
  page_number = c.name("slot[#{slot}]") {
40
51
  Innodb::Page.maybe_undefined(c.get_uint32)
41
52
  }
42
- yield slot, page_number if page_number
53
+ yield slot, page_number
43
54
  end
44
55
  end
45
56
  end
46
57
 
58
+ def each_region
59
+ unless block_given?
60
+ return enum_for(:each_region)
61
+ end
62
+
63
+ super do |region|
64
+ yield region
65
+ end
66
+
67
+ yield({
68
+ :offset => pos_rseg_header,
69
+ :length => size_rseg_header,
70
+ :name => :rseg_header,
71
+ :info => "Rollback Segment Header",
72
+ })
73
+
74
+ (0...UNDO_SEGMENT_SLOTS).each do |slot|
75
+ yield({
76
+ :offset => pos_undo_segment_array + (slot * size_undo_segment_slot),
77
+ :length => size_undo_segment_slot,
78
+ :name => :undo_segment_slot,
79
+ :info => "Undo Segment Slot",
80
+ })
81
+ end
82
+
83
+ nil
84
+ end
85
+
47
86
  def dump
48
87
  super
49
88
 
@@ -15,6 +15,18 @@ class Innodb::Page::TrxSys < Innodb::Page
15
15
  pos_fil_header + size_fil_header
16
16
  end
17
17
 
18
+ def size_trx_sys_header
19
+ 8 + Innodb::FsegEntry::SIZE
20
+ end
21
+
22
+ def pos_rsegs_array
23
+ pos_trx_sys_header + size_trx_sys_header
24
+ end
25
+
26
+ def size_mysql_log_info
27
+ 4 + 8 + 100
28
+ end
29
+
18
30
  # The master's binary log information is located 2000 bytes from the end of
19
31
  # the page.
20
32
  def pos_mysql_master_log_info
@@ -33,14 +45,21 @@ class Innodb::Page::TrxSys < Innodb::Page
33
45
  size - 200
34
46
  end
35
47
 
48
+ def size_doublewrite_info
49
+ Innodb::FsegEntry::SIZE + (2 * (4 + 4 + 4)) + 4
50
+ end
51
+
36
52
  # A magic number present in each MySQL binary log information structure,
37
53
  # which helps identify whether the structure is populated or not.
38
54
  MYSQL_LOG_MAGIC_N = 873422344
39
55
 
56
+ N_RSEGS = 128
57
+
40
58
  def rsegs_array(cursor)
41
- @rsegs_array ||= (0...256).to_a.inject([]) do |a, n|
59
+ @rsegs_array ||= (0...N_RSEGS).to_a.inject([]) do |a, n|
42
60
  cursor.name("slot[#{n}]") do |c|
43
61
  slot = {
62
+ :offset => c.position,
44
63
  :space_id => c.name("space_id") {
45
64
  Innodb::Page.maybe_undefined(c.get_uint32)
46
65
  },
@@ -134,6 +153,55 @@ class Innodb::Page::TrxSys < Innodb::Page
134
153
  def master_log; trx_sys[:master_log]; end
135
154
  def doublewrite; trx_sys[:doublewrite]; end
136
155
 
156
+ def each_region
157
+ unless block_given?
158
+ return enum_for(:each_region)
159
+ end
160
+
161
+ super do |region|
162
+ yield region
163
+ end
164
+
165
+ yield({
166
+ :offset => pos_trx_sys_header,
167
+ :length => size_trx_sys_header,
168
+ :name => :trx_sys_header,
169
+ :info => "Transaction System Header",
170
+ })
171
+
172
+ rsegs.each do |rseg|
173
+ yield({
174
+ :offset => rseg[:offset],
175
+ :length => 4 + 4,
176
+ :name => :rseg,
177
+ :info => "Rollback Segment",
178
+ })
179
+ end
180
+
181
+ yield({
182
+ :offset => pos_mysql_binary_log_info,
183
+ :length => size_mysql_log_info,
184
+ :name => :mysql_binary_log_info,
185
+ :info => "Binary Log Info",
186
+ })
187
+
188
+ yield({
189
+ :offset => pos_mysql_master_log_info,
190
+ :length => size_mysql_log_info,
191
+ :name => :mysql_master_log_info,
192
+ :info => "Master Log Info",
193
+ })
194
+
195
+ yield({
196
+ :offset => pos_doublewrite_info,
197
+ :length => size_doublewrite_info,
198
+ :name => :doublewrite_info,
199
+ :info => "Double Write Buffer Info",
200
+ })
201
+
202
+ nil
203
+ end
204
+
137
205
  # Dump the contents of a page for debugging purposes.
138
206
  def dump
139
207
  super
@@ -17,6 +17,10 @@ class Innodb::Record
17
17
  record[:offset]
18
18
  end
19
19
 
20
+ def length
21
+ record[:length]
22
+ end
23
+
20
24
  def next
21
25
  record[:next]
22
26
  end
@@ -37,6 +41,39 @@ class Innodb::Record
37
41
  row && row.map { |r| "%s=%s" % [r[:name], r[:value].inspect] }.join(", ")
38
42
  end
39
43
 
44
+ def transaction_id
45
+ record[:transaction_id]
46
+ end
47
+
48
+ def roll_pointer
49
+ record[:roll_pointer]
50
+ end
51
+
52
+ def undo
53
+ if innodb_system = @page.space.innodb_system
54
+ undo_space = innodb_system.system_space
55
+ if undo_page = undo_space.page(roll_pointer[:undo_log][:page])
56
+ new_undo_record = Innodb::UndoRecord.new(undo_page, roll_pointer[:undo_log][:offset])
57
+ new_undo_record.index_page = page
58
+ new_undo_record
59
+ end
60
+ end
61
+ end
62
+
63
+ def each_undo_record
64
+ unless block_given?
65
+ return enum_for(:each_undo_record)
66
+ end
67
+
68
+ undo_record = undo
69
+ while undo_record
70
+ yield undo_record
71
+ undo_record = undo_record.prev_by_history
72
+ end
73
+
74
+ nil
75
+ end
76
+
40
77
  def child_page_number
41
78
  record[:child_page_number]
42
79
  end
@@ -54,6 +54,29 @@ class Innodb::Space
54
54
  # The number of pages in the space.
55
55
  attr_reader :pages
56
56
 
57
+ # Return a string which can uniquely identify this space. Be careful not
58
+ # to do anything which could instantiate a BufferCursor so that we can use
59
+ # this method in cursor initialization.
60
+ def name
61
+ return @name if @name
62
+
63
+ prefix = ""
64
+ if File.extname(@file.path) == ".ibd"
65
+ prefix = File.basename(File.dirname(@file.path)) + "/"
66
+ end
67
+
68
+ @name = prefix + File.basename(@file.path)
69
+ end
70
+
71
+ def inspect
72
+ "<%s file=%s, page_size=%i, pages=%i>" % [
73
+ self.class.name,
74
+ name.inspect,
75
+ page_size,
76
+ pages,
77
+ ]
78
+ end
79
+
57
80
  # Read the FSP header "flags" field by byte offset within the space file.
58
81
  # This is useful in order to initialize the page size, as we can't properly
59
82
  # read the FSP_HDR page before we know its size.
@@ -190,10 +213,11 @@ class Innodb::Space
190
213
 
191
214
  def rseg_page?(page_number)
192
215
  if trx_sys
193
- trx_sys.rsegs.include?({
194
- :space_id => 0,
195
- :page_number => page_number,
196
- })
216
+ rseg_match = trx_sys.rsegs.select { |rseg|
217
+ rseg[:space_id] == 0 && rseg[:page_number] == page_number
218
+ }
219
+
220
+ ! rseg_match.empty?
197
221
  end
198
222
  end
199
223
 
@@ -165,4 +165,8 @@ class Innodb::System
165
165
 
166
166
  index
167
167
  end
168
+
169
+ def history
170
+ Innodb::History.new(self)
171
+ end
168
172
  end
@@ -16,24 +16,24 @@ class Innodb::UndoLog
16
16
  8 + 8 + 2 + 2 + 1 + 1 + 8 + 2 + 2 + Innodb::List::NODE_SIZE + size_xa_header
17
17
  end
18
18
 
19
- def read_header(cursor)
20
- cursor.name("header") do |c|
21
- xid_exists = nil
19
+ def header
20
+ @header ||= page.cursor(@position).name("header") do |c|
21
+ xid_flag = nil
22
22
  {
23
23
  :trx_id => c.name("trx_id") { c.get_hex(8) },
24
- :trx_no => c.name("trx_no") { c.get_uint64 },
25
- :delete_marks => c.name("delete_marks") { (c.get_uint16 != 0) },
26
- :log_start => c.name("log_start") { c.get_uint16 },
27
- :xid_exists => c.name("xid_exists") { xid_exists = (c.get_uint8 != 0) },
28
- :dict_trans => c.name("dict_trans") { (c.get_uint8 != 0) },
29
- :table_id => c.name("table_id") { c.get_uint64 },
30
- :next_log => c.name("next_log") { c.get_uint16 },
31
- :prev_log => c.name("prev_log") { c.get_uint16 },
24
+ :trx_no => c.name("trx_no") { c.get_hex(8) },
25
+ :delete_mark_flag => c.name("delete_mark_flag") { (c.get_uint16 != 0) },
26
+ :log_start_offset => c.name("log_start_offset") { c.get_uint16 },
27
+ :xid_flag => c.name("xid_flag") { xid_flag = (c.get_uint8 != 0) },
28
+ :ddl_flag => c.name("ddl_flag") { (c.get_uint8 != 0) },
29
+ :ddl_table_id => c.name("ddl_table_id") { c.get_uint64 },
30
+ :next_log_offset => c.name("next_log_offset") { c.get_uint16 },
31
+ :prev_log_offset => c.name("prev_log_offset") { c.get_uint16 },
32
32
  :history_list_node => c.name("history_list_node") {
33
33
  Innodb::List.get_node(c)
34
34
  },
35
35
  :xid => c.name("xid") {
36
- if xid_exists
36
+ if xid_flag
37
37
  {
38
38
  :format => c.name("format") { c.get_uint32 },
39
39
  :trid_len => c.name("trid_len") { c.get_uint32 },
@@ -46,18 +46,6 @@ class Innodb::UndoLog
46
46
  end
47
47
  end
48
48
 
49
- def undo_log
50
- @undo_log ||= page.cursor(position).name("undo_log") do |c|
51
- {
52
- :header => read_header(c),
53
- }
54
- end
55
- end
56
-
57
- def header
58
- undo_log[:header]
59
- end
60
-
61
49
  def prev_address
62
50
  header[:history_list_node][:prev]
63
51
  end
@@ -66,6 +54,78 @@ class Innodb::UndoLog
66
54
  header[:history_list_node][:next]
67
55
  end
68
56
 
57
+ def undo_record(offset)
58
+ new_undo_record = Innodb::UndoRecord.new(page, offset)
59
+ new_undo_record.undo_log = self
60
+ new_undo_record
61
+ end
62
+
63
+ def min_undo_record
64
+ undo_record(header[:log_start_offset])
65
+ end
66
+
67
+ class UndoRecordCursor
68
+ def initialize(undo_log, offset, direction=:forward)
69
+ @initial = true
70
+ @undo_log = undo_log
71
+ @offset = offset
72
+ @direction = direction
73
+
74
+ case offset
75
+ when :min
76
+ @undo_record = @undo_log.min_undo_record
77
+ when :max
78
+ raise "Not implemented"
79
+ else
80
+ @undo_record = @undo_log.undo_record(offset)
81
+ end
82
+ end
83
+
84
+ def next_undo_record
85
+ if rec = @undo_record.next
86
+ @undo_record = rec
87
+ end
88
+ end
89
+
90
+ def prev_undo_record
91
+ if rec = @undo_record.prev
92
+ @undo_record = rec
93
+ end
94
+ end
95
+
96
+ def undo_record
97
+ if @initial
98
+ @initial = false
99
+ return @undo_record
100
+ end
101
+
102
+ case @direction
103
+ when :forward
104
+ next_undo_record
105
+ when :backward
106
+ prev_undo_record
107
+ end
108
+ end
109
+
110
+ def each_undo_record
111
+ unless block_given?
112
+ return enum_for(:each_undo_record)
113
+ end
114
+
115
+ while rec = undo_record
116
+ yield rec
117
+ end
118
+ end
119
+ end
120
+
121
+ def undo_record_cursor(offset, direction=:forward)
122
+ UndoRecordCursor.new(self, offset, direction)
123
+ end
124
+
125
+ def first_undo_record_cursor
126
+ undo_record_cursor(header[:log_start_offset])
127
+ end
128
+
69
129
  def dump
70
130
  puts "header:"
71
131
  pp header