innodb_ruby 0.9.14 → 0.12.0

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -6
  3. data/bin/innodb_log +13 -18
  4. data/bin/innodb_space +654 -778
  5. data/lib/innodb/checksum.rb +26 -24
  6. data/lib/innodb/data_dictionary.rb +490 -550
  7. data/lib/innodb/data_type.rb +362 -325
  8. data/lib/innodb/field.rb +102 -89
  9. data/lib/innodb/fseg_entry.rb +22 -26
  10. data/lib/innodb/history.rb +21 -21
  11. data/lib/innodb/history_list.rb +72 -76
  12. data/lib/innodb/ibuf_bitmap.rb +36 -36
  13. data/lib/innodb/ibuf_index.rb +6 -2
  14. data/lib/innodb/index.rb +245 -276
  15. data/lib/innodb/inode.rb +166 -124
  16. data/lib/innodb/list.rb +196 -183
  17. data/lib/innodb/log.rb +139 -110
  18. data/lib/innodb/log_block.rb +100 -91
  19. data/lib/innodb/log_group.rb +53 -64
  20. data/lib/innodb/log_reader.rb +97 -96
  21. data/lib/innodb/log_record.rb +328 -279
  22. data/lib/innodb/lsn.rb +86 -81
  23. data/lib/innodb/page/blob.rb +82 -83
  24. data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
  25. data/lib/innodb/page/ibuf_bitmap.rb +34 -34
  26. data/lib/innodb/page/index.rb +965 -924
  27. data/lib/innodb/page/index_compressed.rb +34 -34
  28. data/lib/innodb/page/inode.rb +103 -112
  29. data/lib/innodb/page/sys.rb +13 -15
  30. data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
  31. data/lib/innodb/page/sys_ibuf_header.rb +45 -42
  32. data/lib/innodb/page/sys_rseg_header.rb +88 -82
  33. data/lib/innodb/page/trx_sys.rb +204 -182
  34. data/lib/innodb/page/undo_log.rb +106 -92
  35. data/lib/innodb/page.rb +417 -414
  36. data/lib/innodb/record.rb +121 -164
  37. data/lib/innodb/record_describer.rb +66 -68
  38. data/lib/innodb/space.rb +381 -413
  39. data/lib/innodb/stats.rb +33 -35
  40. data/lib/innodb/system.rb +149 -171
  41. data/lib/innodb/undo_log.rb +129 -107
  42. data/lib/innodb/undo_record.rb +255 -247
  43. data/lib/innodb/util/buffer_cursor.rb +81 -79
  44. data/lib/innodb/util/read_bits_at_offset.rb +2 -1
  45. data/lib/innodb/version.rb +2 -2
  46. data/lib/innodb/xdes.rb +144 -142
  47. data/lib/innodb.rb +4 -5
  48. metadata +100 -25
@@ -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