innodb_ruby 0.9.0 → 0.9.5

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