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,27 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require "innodb/list"
2
3
 
3
4
  # A specialized class for handling INODE pages, which contain index FSEG (file
4
5
  # segment) information. This allows all extents and individual pages assigned
5
6
  # to each index to be found.
6
7
  class Innodb::Page::Inode < Innodb::Page
7
- # The number of "slots" (each representing one page) in the fragment array
8
- # within each Inode entry.
9
- FRAG_ARRAY_N_SLOTS = 32 # FSP_EXTENT_SIZE / 2
10
-
11
- # The size (in bytes) of each slot in the fragment array.
12
- FRAG_SLOT_SIZE = 4
13
-
14
- # A magic number which helps determine if an Inode structure is in use
15
- # and populated with valid data.
16
- MAGIC_N_VALUE = 97937874
17
-
18
8
  # Return the byte offset of the list node, which immediately follows the
19
9
  # FIL header.
20
10
  def pos_list_entry
21
11
  pos_fil_header + size_fil_header
22
12
  end
23
13
 
24
- # Return the byte offset of the list node.
14
+ # Return the size of the list node.
25
15
  def size_list_entry
26
16
  Innodb::List::NODE_SIZE
27
17
  end
@@ -32,21 +22,16 @@ class Innodb::Page::Inode < Innodb::Page
32
22
  pos_list_entry + size_list_entry
33
23
  end
34
24
 
35
- # The size (in bytes) of an Inode entry.
36
- def size_inode
37
- (16 + (3 * Innodb::List::BASE_NODE_SIZE) +
38
- (FRAG_ARRAY_N_SLOTS * FRAG_SLOT_SIZE))
39
- end
40
-
41
25
  # The number of Inode entries that fit on a page.
42
26
  def inodes_per_page
43
- (size - pos_inode_array - 10) / size_inode
27
+ (size - pos_inode_array - 10) / Innodb::Inode::SIZE
44
28
  end
45
29
 
46
30
  # Return the list entry.
47
31
  def list_entry
48
- c = cursor(pos_list_entry)
49
- Innodb::List.get_node(c)
32
+ cursor(pos_list_entry).name("list") do |c|
33
+ Innodb::List.get_node(c)
34
+ end
50
35
  end
51
36
 
52
37
  # Return the "previous" address pointer from the list entry. This is used
@@ -61,40 +46,24 @@ class Innodb::Page::Inode < Innodb::Page
61
46
  list_entry[:next]
62
47
  end
63
48
 
64
- # Read an array of page numbers (32-bit integers, which may be nil) from
65
- # the provided cursor.
66
- def page_number_array(size, cursor)
67
- size.times.map { |n| Innodb::Page.maybe_undefined(cursor.get_uint32) }
68
- end
69
-
70
- # Read a single Inode entry from the provided cursor.
71
- def inode(cursor)
72
- {
73
- :fseg_id => cursor.get_uint64,
74
- :not_full_n_used => cursor.get_uint32,
75
- :free => Innodb::List::Xdes.new(@space,
76
- Innodb::List.get_base_node(cursor)),
77
- :not_full => Innodb::List::Xdes.new(@space,
78
- Innodb::List.get_base_node(cursor)),
79
- :full => Innodb::List::Xdes.new(@space,
80
- Innodb::List.get_base_node(cursor)),
81
- :magic_n => cursor.get_uint32,
82
- :frag_array => page_number_array(FRAG_ARRAY_N_SLOTS, cursor),
83
- }
84
- end
85
-
86
49
  # Read a single Inode entry from the provided byte offset by creating a
87
50
  # cursor and reading the inode using the inode method.
88
- def inode_at(offset)
89
- inode(cursor(offset))
51
+ def inode_at(cursor)
52
+ cursor.name("inode") { |c| Innodb::Inode.new_from_cursor(@space, c) }
90
53
  end
91
54
 
92
55
  # Iterate through all Inodes in the inode array.
93
56
  def each_inode
57
+ unless block_given?
58
+ return enum_for(:each_inode)
59
+ end
60
+
94
61
  inode_cursor = cursor(pos_inode_array)
95
- inodes_per_page.times do
96
- this_inode = inode(inode_cursor)
97
- yield this_inode if this_inode[:fseg_id] != 0
62
+ inodes_per_page.times do |n|
63
+ inode_cursor.name("inode[#{n}]") do |c|
64
+ this_inode = Innodb::Inode.new_from_cursor(@space, c)
65
+ yield this_inode if this_inode.allocated?
66
+ end
98
67
  end
99
68
  end
100
69
 
@@ -107,11 +76,11 @@ class Innodb::Page::Inode < Innodb::Page
107
76
  puts
108
77
 
109
78
  puts "inodes:"
110
- each_inode do |i|
111
- pp i
79
+ each_inode do |inode|
80
+ inode.dump
112
81
  end
113
82
  puts
114
83
  end
115
84
  end
116
85
 
117
- Innodb::Page::SPECIALIZED_CLASSES[:INODE] = Innodb::Page::Inode
86
+ Innodb::Page::SPECIALIZED_CLASSES[:INODE] = Innodb::Page::Inode
@@ -0,0 +1,22 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "innodb/page/sys_rseg_header"
3
+ require "innodb/page/sys_data_dictionary_header"
4
+
5
+ # Another layer of indirection for pages of type SYS, as they have multiple
6
+ # uses within InnoDB. We'll override the self.handle method and check the
7
+ # page offset to decide which type of SYS page this is.
8
+ class Innodb::Page::Sys < Innodb::Page
9
+ def self.handle(page, space, buffer)
10
+ case
11
+ when page.offset == 7
12
+ Innodb::Page::SysDataDictionaryHeader.new(space, buffer)
13
+ when space.rseg_page?(page.offset)
14
+ Innodb::Page::SysRsegHeader.new(space, buffer)
15
+ else
16
+ # We can't do anything better, so pass on the generic InnoDB::Page.
17
+ page
18
+ end
19
+ end
20
+ end
21
+
22
+ Innodb::Page::SPECIALIZED_CLASSES[:SYS] = Innodb::Page::Sys
@@ -0,0 +1,92 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
3
+ # The position of the data dictionary header within the page.
4
+ def pos_data_dictionary_header
5
+ pos_fil_header + size_fil_header
6
+ end
7
+
8
+ # The size of the data dictionary header.
9
+ def size_data_dictionary_header
10
+ ((8 * 3) + (4 * 7) + 4 + Innodb::FsegEntry::SIZE)
11
+ end
12
+
13
+ # Parse the data dictionary header from the page.
14
+ def data_dictionary_header
15
+ cursor(pos_data_dictionary_header).name("data_dictionary_header") do |c|
16
+ {
17
+ :max_row_id => c.name("max_row_id") { c.get_uint64 },
18
+ :max_table_id => c.name("max_table_id") { c.get_uint64 },
19
+ :max_index_id => c.name("max_index_id") { c.get_uint64 },
20
+ :max_space_id => c.name("max_space_id") { c.get_uint32 },
21
+ :unused_mix_id_low => c.name("unused_mix_id_low") { c.get_uint32 },
22
+ :indexes => c.name("indexes") {{
23
+ :SYS_TABLES => c.name("SYS_TABLES") {{
24
+ :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
25
+ :ID => c.name("ID") { c.get_uint32 },
26
+ }},
27
+ :SYS_COLUMNS => c.name("SYS_COLUMNS") {{
28
+ :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
29
+ }},
30
+ :SYS_INDEXES => c.name("SYS_INDEXES") {{
31
+ :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
32
+ }},
33
+ :SYS_FIELDS => c.name("SYS_FIELDS") {{
34
+ :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
35
+ }}
36
+ }},
37
+ :unused_space => c.name("unused_space") { c.get_bytes(4) },
38
+ :fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
39
+ }
40
+ end
41
+ end
42
+
43
+ # Iterate through all indexes in the data dictionary, yielding the table
44
+ # name, index name, and the index itself as an Innodb::Index.
45
+ def each_index
46
+ unless block_given?
47
+ return enum_for(:each_index)
48
+ end
49
+
50
+ data_dictionary_header[:indexes].each do |table_name, indexes|
51
+ indexes.each do |index_name, root_page_number|
52
+ yield table_name, index_name, @space.index(root_page_number)
53
+ end
54
+ end
55
+ end
56
+
57
+ def dump
58
+ super
59
+
60
+ puts
61
+ puts "data_dictionary header:"
62
+ pp data_dictionary_header
63
+ end
64
+
65
+ # A record describer for SYS_TABLES clustered records.
66
+ class SYS_TABLES_PRIMARY
67
+ def self.cursor_sendable_description(page)
68
+ {
69
+ :type => :clustered,
70
+ :key => [
71
+ ["VARCHAR(100)", :NOT_NULL], # NAME
72
+ ],
73
+ :row => [
74
+ [:BIGINT, :UNSIGNED, :NOT_NULL], # ID
75
+ [:INT, :UNSIGNED, :NOT_NULL], # N_COLS
76
+ [:INT, :UNSIGNED, :NOT_NULL], # TYPE
77
+ [:BIGINT, :UNSIGNED, :NOT_NULL], # MIX_ID
78
+ [:INT, :UNSIGNED, :NOT_NULL], # MIX_LEN
79
+ ["VARCHAR(100)"], # CLUSTER_NAME
80
+ [:INT, :UNSIGNED, :NOT_NULL], # SPACE
81
+ ]
82
+ }
83
+ end
84
+ end
85
+
86
+ RECORD_DESCRIBERS = {
87
+ :SYS_TABLES => { :PRIMARY => SYS_TABLES_PRIMARY, :ID => nil },
88
+ :SYS_COLUMNS => { :PRIMARY => nil },
89
+ :SYS_INDEXES => { :PRIMARY => nil },
90
+ :SYS_FIELDS => { :PRIMARY => nil },
91
+ }
92
+ end
@@ -0,0 +1,59 @@
1
+ # -*- encoding : utf-8 -*-
2
+ class Innodb::Page::SysRsegHeader < Innodb::Page
3
+ # The position of the rollback segment header within the page.
4
+ def pos_rseg_header
5
+ pos_fil_header + size_fil_header
6
+ end
7
+
8
+ # The size of the rollback segment header.
9
+ def size_rseg_header
10
+ 4 + 4 + Innodb::List::BASE_NODE_SIZE + Innodb::FsegEntry::SIZE
11
+ end
12
+
13
+ def pos_undo_segment_array
14
+ pos_rseg_header + size_rseg_header
15
+ end
16
+
17
+ # Parse the rollback segment header from the page.
18
+ def rseg_header
19
+ cursor(pos_rseg_header).name("rseg_header") do |c|
20
+ {
21
+ :max_size => c.name("max_size") { c.get_uint32 },
22
+ :history_size => c.name("history_size") { c.get_uint32 },
23
+ :history_list => c.name("history_list") {
24
+ Innodb::List::History.new(@space, Innodb::List.get_base_node(c))
25
+ },
26
+ :fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
27
+ }
28
+ end
29
+ end
30
+
31
+ def each_undo_segment
32
+ unless block_given?
33
+ return enum_for(:each_undo_segment)
34
+ end
35
+
36
+ cursor(pos_undo_segment_array).name("undo_segment_array") do |c|
37
+ (0...1023).each do |slot|
38
+ page_number = c.name("slot[#{slot}]") {
39
+ Innodb::Page.maybe_undefined(c.get_uint32)
40
+ }
41
+ yield slot, page_number if page_number
42
+ end
43
+ end
44
+ end
45
+
46
+ def dump
47
+ super
48
+
49
+ puts
50
+ puts "rollback segment header:"
51
+ pp rseg_header
52
+
53
+ puts
54
+ puts "undo segment array:"
55
+ each_undo_segment do |slot, page_number|
56
+ puts " #{slot}: #{page_number}"
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  # A specialized class for TRX_SYS pages, which contain various information
2
3
  # about the transaction system within InnoDB. Only one TRX_SYS page exists in
3
4
  # any given InnoDB installation, and it is page 5 of the system tablespace
@@ -35,14 +36,34 @@ class Innodb::Page::TrxSys < Innodb::Page
35
36
  # which helps identify whether the structure is populated or not.
36
37
  MYSQL_LOG_MAGIC_N = 873422344
37
38
 
39
+ def rsegs_array(cursor)
40
+ @rsegs_array ||= (0...256).to_a.inject([]) do |a, n|
41
+ cursor.name("slot[#{n}]") do |c|
42
+ slot = {
43
+ :space_id => c.name("space_id") {
44
+ Innodb::Page.maybe_undefined(c.get_uint32)
45
+ },
46
+ :page_number => c.name("page_number") {
47
+ Innodb::Page.maybe_undefined(c.get_uint32)
48
+ },
49
+ }
50
+ if slot[:space_id] && slot[:page_number]
51
+ a << slot
52
+ end
53
+ end
54
+ a
55
+ end
56
+ end
57
+
38
58
  # Read a MySQL binary log information structure from a given position.
39
- def mysql_log_info(offset)
40
- c = cursor(offset)
41
- if c.get_uint32 == MYSQL_LOG_MAGIC_N
42
- {
43
- :offset => c.get_uint64,
44
- :name => c.get_bytes(100),
45
- }
59
+ def mysql_log_info(cursor, offset)
60
+ cursor.peek(offset) do |c|
61
+ if c.name("magic_n") { c.get_uint32 } == MYSQL_LOG_MAGIC_N
62
+ {
63
+ :offset => c.name("offset") { c.get_uint64 },
64
+ :name => c.name("name") { c.get_bytes(100) },
65
+ }
66
+ end
46
67
  end
47
68
  end
48
69
 
@@ -53,10 +74,10 @@ class Innodb::Page::TrxSys < Innodb::Page
53
74
  # Read a single doublewrite buffer information structure from a given cursor.
54
75
  def doublewrite_page_info(cursor)
55
76
  {
56
- :magic_n => cursor.get_uint32,
77
+ :magic_n => cursor.name("magic_n") { cursor.get_uint32 },
57
78
  :page_number => [
58
- cursor.get_uint32,
59
- cursor.get_uint32,
79
+ cursor.name("page[0]") { cursor.get_uint32 },
80
+ cursor.name("page[1]") { cursor.get_uint32 },
60
81
  ],
61
82
  }
62
83
  end
@@ -66,30 +87,52 @@ class Innodb::Page::TrxSys < Innodb::Page
66
87
  DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N = 1783657386
67
88
 
68
89
  # Read the overall doublewrite buffer structures
69
- def doublewrite_info
70
- c = cursor(pos_doublewrite_info)
71
- {
72
- :fseg => Innodb::FsegEntry.get_inode(@space, c),
73
- :page_info => [
74
- doublewrite_page_info(c),
75
- doublewrite_page_info(c),
76
- ],
77
- :space_id_stored => c.get_uint32 == DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N,
78
- }
90
+ def doublewrite_info(cursor)
91
+ cursor.peek(pos_doublewrite_info) do |c_doublewrite|
92
+ c_doublewrite.name("doublewrite") do |c|
93
+ {
94
+ :fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
95
+ :page_info => [
96
+ c.name("group[0]") { doublewrite_page_info(c) },
97
+ c.name("group[1]") { doublewrite_page_info(c) },
98
+ ],
99
+ :space_id_stored =>
100
+ (c.name("space_id_stored") { c.get_uint32 } ==
101
+ DOUBLEWRITE_SPACE_ID_STORED_MAGIC_N),
102
+ }
103
+ end
104
+ end
79
105
  end
80
106
 
81
107
  # Read the TRX_SYS headers and other information.
82
108
  def trx_sys
83
- c = cursor(pos_trx_sys_header)
84
- @trx_sys ||= {
85
- :trx_id => c.get_uint64,
86
- :fseg => Innodb::FsegEntry.get_inode(@space, c),
87
- :binary_log => mysql_log_info(pos_mysql_binary_log_info),
88
- :master_log => mysql_log_info(pos_mysql_master_log_info),
89
- :doublewrite => doublewrite_info,
90
- }
109
+ @trx_sys ||= cursor(pos_trx_sys_header).name("trx_sys") do |c|
110
+ {
111
+ :trx_id => c.name("trx_id") { c.get_uint64 },
112
+ :fseg => c.name("fseg") {
113
+ Innodb::FsegEntry.get_inode(@space, c)
114
+ },
115
+ :rsegs => c.name("rsegs") {
116
+ rsegs_array(c)
117
+ },
118
+ :binary_log => c.name("binary_log") {
119
+ mysql_log_info(c, pos_mysql_binary_log_info)
120
+ },
121
+ :master_log => c.name("master_log") {
122
+ mysql_log_info(c, pos_mysql_master_log_info)
123
+ },
124
+ :doublewrite => doublewrite_info(c),
125
+ }
126
+ end
91
127
  end
92
128
 
129
+ def trx_id; trx_sys[:trx_id]; end
130
+ def fseg; trx_sys[:fseg]; end
131
+ def rsegs; trx_sys[:rsegs]; end
132
+ def binary_log; trx_sys[:binary_log]; end
133
+ def master_log; trx_sys[:master_log]; end
134
+ def doublewrite; trx_sys[:doublewrite]; end
135
+
93
136
  # Dump the contents of a page for debugging purposes.
94
137
  def dump
95
138
  super
@@ -100,4 +143,4 @@ class Innodb::Page::TrxSys < Innodb::Page
100
143
  end
101
144
  end
102
145
 
103
- Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
146
+ Innodb::Page::SPECIALIZED_CLASSES[:TRX_SYS] = Innodb::Page::TrxSys
@@ -0,0 +1,95 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Innodb::Page::UndoLog < Innodb::Page
4
+ def pos_undo_page_header
5
+ pos_fil_header + size_fil_header
6
+ end
7
+
8
+ def size_undo_page_header
9
+ 2 + 2 + 2 + Innodb::List::NODE_SIZE
10
+ end
11
+
12
+ def pos_undo_segment_header
13
+ pos_undo_page_header + size_undo_page_header
14
+ end
15
+
16
+ def size_undo_segment_header
17
+ 2 + 2 + Innodb::FsegEntry::SIZE + Innodb::List::BASE_NODE_SIZE
18
+ end
19
+
20
+ def pos_undo_logs
21
+ pos_undo_segment_header + size_undo_segment_header
22
+ end
23
+
24
+ UNDO_PAGE_TYPES = {
25
+ 1 => :insert,
26
+ 2 => :update,
27
+ }
28
+
29
+ UNDO_SEGMENT_STATES = {
30
+ 1 => :active,
31
+ 2 => :cached,
32
+ 3 => :to_free,
33
+ 4 => :to_purge,
34
+ 5 => :prepared,
35
+ }
36
+
37
+ def undo_page_header
38
+ @undo_page_header ||=
39
+ cursor(pos_undo_page_header).name("undo_page_header") do |c|
40
+ {
41
+ :type => c.name("type") { UNDO_PAGE_TYPES[c.get_uint16] },
42
+ :latest_log_record_offset => c.name("latest_log_record_offset") { c.get_uint16 },
43
+ :free_offset => c.name("free_offset") { c.get_uint16 },
44
+ :page_list_node => c.name("page_list") { Innodb::List.get_node(c) },
45
+ }
46
+ end
47
+ end
48
+
49
+ def prev_address
50
+ undo_page_header[:page_list_node][:prev]
51
+ end
52
+
53
+ def next_address
54
+ undo_page_header[:page_list_node][:next]
55
+ end
56
+
57
+ def undo_segment_header
58
+ @undo_segment_header ||=
59
+ cursor(pos_undo_segment_header).name("undo_segment_header") do |c|
60
+ {
61
+ :state => c.name("state") { UNDO_SEGMENT_STATES[c.get_uint16] },
62
+ :last_log_offset => c.name("last_log_offset") { c.get_uint16 },
63
+ :fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
64
+ :page_list => c.name("page_list") {
65
+ Innodb::List::UndoPage.new(@space, Innodb::List.get_base_node(c))
66
+ },
67
+ }
68
+ end
69
+ end
70
+
71
+ def undo_log(pos)
72
+ Innodb::UndoLog.new(self, pos)
73
+ end
74
+
75
+ # Dump the contents of a page for debugging purposes.
76
+ def dump
77
+ super
78
+
79
+ puts "undo page header:"
80
+ pp undo_page_header
81
+ puts
82
+
83
+ puts "undo segment header:"
84
+ pp undo_segment_header
85
+ puts
86
+
87
+ puts "last undo log:"
88
+ if undo_segment_header[:last_log_offset] != 0
89
+ undo_log(undo_segment_header[:last_log_offset]).dump
90
+ end
91
+ puts
92
+ end
93
+ end
94
+
95
+ Innodb::Page::SPECIALIZED_CLASSES[:UNDO_LOG] = Innodb::Page::UndoLog