innodb_ruby 0.9.16 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +377 -757
  5. data/lib/innodb.rb +4 -5
  6. data/lib/innodb/checksum.rb +26 -24
  7. data/lib/innodb/data_dictionary.rb +490 -550
  8. data/lib/innodb/data_type.rb +362 -326
  9. data/lib/innodb/field.rb +102 -89
  10. data/lib/innodb/fseg_entry.rb +22 -26
  11. data/lib/innodb/history.rb +21 -21
  12. data/lib/innodb/history_list.rb +72 -76
  13. data/lib/innodb/ibuf_bitmap.rb +36 -36
  14. data/lib/innodb/ibuf_index.rb +6 -2
  15. data/lib/innodb/index.rb +245 -276
  16. data/lib/innodb/inode.rb +154 -155
  17. data/lib/innodb/list.rb +191 -183
  18. data/lib/innodb/log.rb +139 -110
  19. data/lib/innodb/log_block.rb +100 -91
  20. data/lib/innodb/log_group.rb +53 -64
  21. data/lib/innodb/log_reader.rb +97 -96
  22. data/lib/innodb/log_record.rb +328 -279
  23. data/lib/innodb/lsn.rb +86 -81
  24. data/lib/innodb/page.rb +417 -414
  25. data/lib/innodb/page/blob.rb +82 -83
  26. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  27. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  28. data/lib/innodb/page/index.rb +964 -943
  29. data/lib/innodb/page/index_compressed.rb +34 -34
  30. data/lib/innodb/page/inode.rb +103 -112
  31. data/lib/innodb/page/sys.rb +13 -15
  32. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  33. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  34. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  35. data/lib/innodb/page/trx_sys.rb +204 -182
  36. data/lib/innodb/page/undo_log.rb +106 -92
  37. data/lib/innodb/record.rb +121 -160
  38. data/lib/innodb/record_describer.rb +66 -68
  39. data/lib/innodb/space.rb +380 -418
  40. data/lib/innodb/stats.rb +33 -35
  41. data/lib/innodb/system.rb +149 -171
  42. data/lib/innodb/undo_log.rb +129 -107
  43. data/lib/innodb/undo_record.rb +255 -247
  44. data/lib/innodb/util/buffer_cursor.rb +81 -79
  45. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  46. data/lib/innodb/version.rb +2 -2
  47. data/lib/innodb/xdes.rb +144 -142
  48. metadata +80 -11
@@ -1,46 +1,46 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # This is horribly incomplete and broken. InnoDB compression does not
4
4
  # currently work in innodb_ruby. Patches are welcome!
5
5
  # (Hint hint, nudge nudge, Facebook developers!)
6
6
 
7
- class Innodb::Page::Index::Compressed < Innodb::Page::Index
8
- # The number of directory slots in use.
9
- def directory_slots
10
- page_header[:n_heap] - 2
11
- end
7
+ module Innodb
8
+ class Page
9
+ class IndexCompressed < Index
10
+ specialization_for({ type: :INDEX, compressed: true })
12
11
 
13
- def directory
14
- super.map { |n| n & 0x3fff }
15
- end
12
+ # The number of directory slots in use.
13
+ def directory_slots
14
+ page_header[:n_heap] - 2
15
+ end
16
16
 
17
- def uncompressed_columns_size
18
- if level == 0
19
- if record_format && record_format[:type] == :clustered
20
- 6 + 7 # Transaction ID + Roll Pointer
21
- else
22
- 0
17
+ def directory
18
+ super.map { |n| n & 0x3fff }
23
19
  end
24
- else
25
- 4 # Node pointer for non-leaf pages
26
- end
27
- end
28
20
 
29
- # Return the amount of free space in the page.
30
- def free_space
31
- free_space_start = size - size_fil_trailer - directory_space -
32
- (uncompressed_columns_size * (page_header[:n_heap] - 2))
33
- puts "Free space start == %04x" % [offset * size + free_space_start]
34
- c = cursor(free_space_start).backward
35
- zero_bytes = 0
36
- while (b = c.get_uint8) == 0
37
- zero_bytes += 1
21
+ def uncompressed_columns_size
22
+ if leaf?
23
+ if record_format && record_format[:type] == :clustered
24
+ 6 + 7 # Transaction ID + Roll Pointer
25
+ else
26
+ 0
27
+ end
28
+ else
29
+ 4 # Node pointer for non-leaf pages
30
+ end
31
+ end
32
+
33
+ # Return the amount of free space in the page.
34
+ def free_space
35
+ free_space_start =
36
+ size - size_fil_trailer - directory_space - (uncompressed_columns_size * (page_header.n_heap - 2))
37
+ puts "Free space start == %04x" % [offset * size + free_space_start]
38
+ c = cursor(free_space_start).backward
39
+ zero_bytes = 0
40
+ zero_bytes += 1 while c.read_uint8.zero?
41
+ zero_bytes
42
+ # page_header[:garbage] + (size - size_fil_trailer - directory_space - page_header[:heap_top])
43
+ end
38
44
  end
39
- zero_bytes
40
- #page_header[:garbage] +
41
- # (size - size_fil_trailer - directory_space - page_header[:heap_top])
42
45
  end
43
46
  end
44
-
45
- Innodb::Page::SPECIALIZED_CLASSES[{:type => :INDEX, :compressed => true}] = Innodb::Page::Index::Compressed
46
-
@@ -1,139 +1,130 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "innodb/list"
4
4
 
5
5
  # A specialized class for handling INODE pages, which contain index FSEG (file
6
6
  # segment) information. This allows all extents and individual pages assigned
7
7
  # to each index to be found.
8
- class Innodb::Page::Inode < Innodb::Page
9
- # Return the byte offset of the list node, which immediately follows the
10
- # FIL header.
11
- def pos_list_entry
12
- pos_page_body
13
- end
14
-
15
- # Return the size of the list node.
16
- def size_list_entry
17
- Innodb::List::NODE_SIZE
18
- end
19
-
20
- # Return the byte offset of the Inode array in the page, which immediately
21
- # follows the list entry.
22
- def pos_inode_array
23
- pos_list_entry + size_list_entry
24
- end
25
-
26
- # The number of Inode entries that fit on a page.
27
- def inodes_per_page
28
- (size - pos_inode_array - 10) / Innodb::Inode::SIZE
29
- end
8
+ module Innodb
9
+ class Page
10
+ class Inode < Page
11
+ specialization_for :INODE
12
+
13
+ # Return the byte offset of the list node, which immediately follows the
14
+ # FIL header.
15
+ def pos_list_entry
16
+ pos_page_body
17
+ end
30
18
 
31
- def size_inode_array
32
- inodes_per_page * Innodb::Inode::SIZE
33
- end
19
+ # Return the size of the list node.
20
+ def size_list_entry
21
+ Innodb::List::NODE_SIZE
22
+ end
34
23
 
35
- # Return the list entry.
36
- def list_entry
37
- cursor(pos_list_entry).name("list") do |c|
38
- Innodb::List.get_node(c)
39
- end
40
- end
24
+ # Return the byte offset of the Inode array in the page, which immediately
25
+ # follows the list entry.
26
+ def pos_inode_array
27
+ pos_list_entry + size_list_entry
28
+ end
41
29
 
42
- # Return the "previous" address pointer from the list entry. This is used
43
- # by Innodb::List::Inode to iterate through Inode lists.
44
- def prev_address
45
- list_entry[:prev]
46
- end
30
+ # The number of Inode entries that fit on a page.
31
+ def inodes_per_page
32
+ (size - pos_inode_array - 10) / Innodb::Inode::SIZE
33
+ end
47
34
 
48
- # Return the "next" address pointer from the list entry. This is used
49
- # by Innodb::List::Inode to iterate through Inode lists.
50
- def next_address
51
- list_entry[:next]
52
- end
35
+ def size_inode_array
36
+ inodes_per_page * Innodb::Inode::SIZE
37
+ end
53
38
 
54
- # Read a single Inode entry from the provided byte offset by creating a
55
- # cursor and reading the inode using the inode method.
56
- def inode_at(cursor)
57
- cursor.name("inode[#{cursor.position}]") { |c| Innodb::Inode.new_from_cursor(@space, c) }
58
- end
39
+ # Return the list entry.
40
+ def list_entry
41
+ cursor(pos_list_entry).name("list") { |c| Innodb::List.get_node(c) }
42
+ end
59
43
 
60
- # Iterate through all Inodes in the inode array.
61
- def each_inode
62
- unless block_given?
63
- return enum_for(:each_inode)
64
- end
44
+ # Return the "previous" address pointer from the list entry. This is used
45
+ # by Innodb::List::Inode to iterate through Inode lists.
46
+ def prev_address
47
+ list_entry.prev
48
+ end
65
49
 
66
- inode_cursor = cursor(pos_inode_array)
67
- inodes_per_page.times do |n|
68
- inode_cursor.name("inode[#{n}]") do |c|
69
- this_inode = Innodb::Inode.new_from_cursor(@space, c)
70
- yield this_inode
50
+ # Return the "next" address pointer from the list entry. This is used
51
+ # by Innodb::List::Inode to iterate through Inode lists.
52
+ def next_address
53
+ list_entry.next
71
54
  end
72
- end
73
- end
74
55
 
75
- # Iterate through all allocated inodes in the inode array.
76
- def each_allocated_inode
77
- unless block_given?
78
- return enum_for(:each_allocated_inode)
79
- end
56
+ # Read a single Inode entry from the provided byte offset by creating a
57
+ # cursor and reading the inode using the inode method.
58
+ def inode_at(cursor)
59
+ cursor.name("inode[#{cursor.position}]") { |c| Innodb::Inode.new_from_cursor(@space, c) }
60
+ end
80
61
 
81
- each_inode do |this_inode|
82
- yield this_inode if this_inode.allocated?
83
- end
84
- end
62
+ # Iterate through all Inodes in the inode array.
63
+ def each_inode
64
+ return enum_for(:each_inode) unless block_given?
85
65
 
86
- def each_region
87
- unless block_given?
88
- return enum_for(:each_region)
89
- end
66
+ inode_cursor = cursor(pos_inode_array)
67
+ inodes_per_page.times do |n|
68
+ inode_cursor.name("inode[#{n}]") do |c|
69
+ yield Innodb::Inode.new_from_cursor(@space, c)
70
+ end
71
+ end
72
+ end
90
73
 
91
- super do |region|
92
- yield region
93
- end
74
+ # Iterate through all allocated inodes in the inode array.
75
+ def each_allocated_inode
76
+ return enum_for(:each_allocated_inode) unless block_given?
94
77
 
95
- yield({
96
- :offset => pos_list_entry,
97
- :length => size_list_entry,
98
- :name => :list_entry,
99
- :info => "Inode List Entry",
100
- })
101
-
102
- each_inode do |inode|
103
- if inode.allocated?
104
- yield({
105
- :offset => inode.offset,
106
- :length => Innodb::Inode::SIZE,
107
- :name => :inode_used,
108
- :info => "Inode (used)",
109
- })
110
- else
111
- yield({
112
- :offset => inode.offset,
113
- :length => Innodb::Inode::SIZE,
114
- :name => :inode_free,
115
- :info => "Inode (free)",
116
- })
78
+ each_inode do |this_inode|
79
+ yield this_inode if this_inode.allocated?
80
+ end
117
81
  end
118
- end
119
82
 
120
- nil
121
- end
83
+ def each_region(&block)
84
+ return enum_for(:each_region) unless block_given?
85
+
86
+ super(&block)
87
+
88
+ yield Region.new(
89
+ offset: pos_list_entry,
90
+ length: size_list_entry,
91
+ name: :list_entry,
92
+ info: "Inode List Entry"
93
+ )
94
+
95
+ each_inode do |inode|
96
+ if inode.allocated?
97
+ yield Region.new(
98
+ offset: inode.offset,
99
+ length: Innodb::Inode::SIZE,
100
+ name: :inode_used,
101
+ info: "Inode (used)"
102
+ )
103
+ else
104
+ yield Region.new(
105
+ offset: inode.offset,
106
+ length: Innodb::Inode::SIZE,
107
+ name: :inode_free,
108
+ info: "Inode (free)"
109
+ )
110
+ end
111
+ end
112
+
113
+ nil
114
+ end
122
115
 
123
- # Dump the contents of a page for debugging purposes.
124
- def dump
125
- super
116
+ # Dump the contents of a page for debugging purposes.
117
+ def dump
118
+ super
126
119
 
127
- puts "list entry:"
128
- pp list_entry
129
- puts
120
+ puts "list entry:"
121
+ pp list_entry
122
+ puts
130
123
 
131
- puts "inodes:"
132
- each_inode do |inode|
133
- inode.dump
124
+ puts "inodes:"
125
+ each_inode(&:dump)
126
+ puts
127
+ end
134
128
  end
135
- puts
136
129
  end
137
130
  end
138
-
139
- Innodb::Page::SPECIALIZED_CLASSES[:INODE] = Innodb::Page::Inode
@@ -1,4 +1,4 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "innodb/page/sys_rseg_header"
4
4
  require "innodb/page/sys_data_dictionary_header"
@@ -7,20 +7,18 @@ require "innodb/page/sys_ibuf_header"
7
7
  # Another layer of indirection for pages of type SYS, as they have multiple
8
8
  # uses within InnoDB. We'll override the self.handle method and check the
9
9
  # page offset to decide which type of SYS page this is.
10
- class Innodb::Page::Sys < Innodb::Page
11
- def self.handle(page, space, buffer, page_number=nil)
12
- case
13
- when page.offset == 3
14
- Innodb::Page::SysIbufHeader.new(space, buffer, page_number)
15
- when page.offset == 7
16
- Innodb::Page::SysDataDictionaryHeader.new(space, buffer, page_number)
17
- when space.rseg_page?(page.offset)
18
- Innodb::Page::SysRsegHeader.new(space, buffer, page_number)
19
- else
20
- # We can't do anything better, so pass on the generic InnoDB::Page.
21
- page
10
+ module Innodb
11
+ class Page
12
+ class Sys < Page
13
+ specialization_for :SYS
14
+
15
+ def self.handle(page, space, buffer, page_number = nil)
16
+ return Innodb::Page::SysIbufHeader.new(space, buffer, page_number) if page.offset == 3
17
+ return Innodb::Page::SysDataDictionaryHeader.new(space, buffer, page_number) if page.offset == 7
18
+ return Innodb::Page::SysRsegHeader.new(space, buffer, page_number) if space.rseg_page?(page.offset)
19
+
20
+ page
21
+ end
22
22
  end
23
23
  end
24
24
  end
25
-
26
- Innodb::Page::SPECIALIZED_CLASSES[:SYS] = Innodb::Page::Sys
@@ -1,70 +1,92 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- class Innodb::Page::SysDataDictionaryHeader < Innodb::Page
4
- # The position of the data dictionary header within the page.
5
- def pos_data_dictionary_header
6
- pos_page_body
7
- end
3
+ module Innodb
4
+ class Page
5
+ class SysDataDictionaryHeader < Page
6
+ Header = Struct.new(
7
+ :max_row_id,
8
+ :max_table_id,
9
+ :max_index_id,
10
+ :max_space_id,
11
+ :unused_mix_id_low,
12
+ :indexes,
13
+ :unused_space,
14
+ :fseg,
15
+ keyword_init: true
16
+ )
8
17
 
9
- # The size of the data dictionary header.
10
- def size_data_dictionary_header
11
- ((8 * 3) + (4 * 7) + 4 + Innodb::FsegEntry::SIZE)
12
- end
18
+ # The position of the data dictionary header within the page.
19
+ def pos_data_dictionary_header
20
+ pos_page_body
21
+ end
13
22
 
14
- # Parse the data dictionary header from the page.
15
- def data_dictionary_header
16
- cursor(pos_data_dictionary_header).name("data_dictionary_header") do |c|
17
- {
18
- :max_row_id => c.name("max_row_id") { c.get_uint64 },
19
- :max_table_id => c.name("max_table_id") { c.get_uint64 },
20
- :max_index_id => c.name("max_index_id") { c.get_uint64 },
21
- :max_space_id => c.name("max_space_id") { c.get_uint32 },
22
- :unused_mix_id_low => c.name("unused_mix_id_low") { c.get_uint32 },
23
- :indexes => c.name("indexes") {{
24
- :SYS_TABLES => c.name("SYS_TABLES") {{
25
- :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
26
- :ID => c.name("ID") { c.get_uint32 },
27
- }},
28
- :SYS_COLUMNS => c.name("SYS_COLUMNS") {{
29
- :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
30
- }},
31
- :SYS_INDEXES => c.name("SYS_INDEXES") {{
32
- :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
33
- }},
34
- :SYS_FIELDS => c.name("SYS_FIELDS") {{
35
- :PRIMARY => c.name("PRIMARY") { c.get_uint32 },
36
- }}
37
- }},
38
- :unused_space => c.name("unused_space") { c.get_bytes(4) },
39
- :fseg => c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) },
40
- }
41
- end
42
- end
23
+ # The size of the data dictionary header.
24
+ def size_data_dictionary_header
25
+ ((8 * 3) + (4 * 7) + 4 + Innodb::FsegEntry::SIZE)
26
+ end
43
27
 
44
- def each_region
45
- unless block_given?
46
- return enum_for(:each_region)
47
- end
28
+ # Parse the data dictionary header from the page.
29
+ def data_dictionary_header
30
+ cursor(pos_data_dictionary_header).name("data_dictionary_header") do |c|
31
+ Header.new(
32
+ max_row_id: c.name("max_row_id") { c.read_uint64 },
33
+ max_table_id: c.name("max_table_id") { c.read_uint64 },
34
+ max_index_id: c.name("max_index_id") { c.read_uint64 },
35
+ max_space_id: c.name("max_space_id") { c.read_uint32 },
36
+ unused_mix_id_low: c.name("unused_mix_id_low") { c.read_uint32 },
37
+ indexes: c.name("indexes") do
38
+ {
39
+ SYS_TABLES: c.name("SYS_TABLES") do
40
+ {
41
+ PRIMARY: c.name("PRIMARY") { c.read_uint32 },
42
+ ID: c.name("ID") { c.read_uint32 },
43
+ }
44
+ end,
45
+ SYS_COLUMNS: c.name("SYS_COLUMNS") do
46
+ {
47
+ PRIMARY: c.name("PRIMARY") { c.read_uint32 },
48
+ }
49
+ end,
50
+ SYS_INDEXES: c.name("SYS_INDEXES") do
51
+ {
52
+ PRIMARY: c.name("PRIMARY") { c.read_uint32 },
53
+ }
54
+ end,
55
+ SYS_FIELDS: c.name("SYS_FIELDS") do
56
+ {
57
+ PRIMARY: c.name("PRIMARY") { c.read_uint32 },
58
+ }
59
+ end,
60
+ }
61
+ end,
62
+ unused_space: c.name("unused_space") { c.read_bytes(4) },
63
+ fseg: c.name("fseg") { Innodb::FsegEntry.get_inode(@space, c) }
64
+ )
65
+ end
66
+ end
48
67
 
49
- super do |region|
50
- yield region
51
- end
68
+ def each_region(&block)
69
+ return enum_for(:each_region) unless block_given?
52
70
 
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
- })
71
+ super(&block)
59
72
 
60
- nil
61
- end
73
+ yield Region.new(
74
+ offset: pos_data_dictionary_header,
75
+ length: size_data_dictionary_header,
76
+ name: :data_dictionary_header,
77
+ info: "Data Dictionary Header"
78
+ )
62
79
 
63
- def dump
64
- super
80
+ nil
81
+ end
65
82
 
66
- puts
67
- puts "data_dictionary header:"
68
- pp data_dictionary_header
83
+ def dump
84
+ super
85
+
86
+ puts
87
+ puts "data_dictionary header:"
88
+ pp data_dictionary_header
89
+ end
90
+ end
69
91
  end
70
92
  end