innodb_ruby 0.9.16 → 0.11.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 +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
data/lib/innodb/field.rb CHANGED
@@ -1,114 +1,127 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "innodb/data_type"
4
4
 
5
5
  # A single field in an InnoDB record (within an INDEX page). This class
6
6
  # provides essential information to parse records, including the length
7
7
  # of the fixed-width and variable-width portion of the field.
8
- class Innodb::Field
9
- attr_reader :position, :name, :data_type, :nullable
10
-
11
- # Size of a reference to data stored externally to the page.
12
- EXTERN_FIELD_SIZE = 20
13
-
14
- def initialize(position, name, type_definition, *properties)
15
- @position = position
16
- @name = name
17
- @nullable = properties.delete(:NOT_NULL) ? false : true
18
- base_type, modifiers = parse_type_definition(type_definition.to_s)
19
- @data_type = Innodb::DataType.new(base_type, modifiers, properties)
20
- end
8
+ module Innodb
9
+ class Field
10
+ ExternReference = Struct.new(
11
+ :space_id,
12
+ :page_number,
13
+ :offset,
14
+ :length, # rubocop:disable Lint/StructNewOverride
15
+ keyword_init: true
16
+ )
17
+
18
+ attr_reader :position
19
+ attr_reader :name
20
+ attr_reader :data_type
21
+ attr_reader :nullable
22
+
23
+ # Size of a reference to data stored externally to the page.
24
+ EXTERN_FIELD_SIZE = 20
25
+
26
+ def initialize(position, name, type_definition, *properties)
27
+ @position = position
28
+ @name = name
29
+ @nullable = !properties.delete(:NOT_NULL)
30
+ base_type, modifiers = parse_type_definition(type_definition.to_s)
31
+ @data_type = Innodb::DataType.new(base_type, modifiers, properties)
32
+ end
21
33
 
22
- # Return whether this field can be NULL.
23
- def nullable?
24
- @nullable
25
- end
34
+ # Return whether this field can be NULL.
35
+ def nullable?
36
+ @nullable
37
+ end
26
38
 
27
- # Return whether this field is NULL.
28
- def null?(record)
29
- nullable? && record[:header][:nulls].include?(@name)
30
- end
39
+ # Return whether this field is NULL.
40
+ def null?(record)
41
+ nullable? && record.header.nulls.include?(@name)
42
+ end
31
43
 
32
- # Return whether a part of this field is stored externally (off-page).
33
- def extern?(record)
34
- record[:header][:externs].include?(@name)
35
- end
44
+ # Return whether a part of this field is stored externally (off-page).
45
+ def extern?(record)
46
+ record.header.externs.include?(@name)
47
+ end
36
48
 
37
- def variable?
38
- @data_type.is_a? Innodb::DataType::BlobType or
39
- @data_type.is_a? Innodb::DataType::VariableBinaryType or
40
- @data_type.is_a? Innodb::DataType::VariableCharacterType
41
- end
49
+ def variable?
50
+ [
51
+ Innodb::DataType::BlobType,
52
+ Innodb::DataType::VariableBinaryType,
53
+ Innodb::DataType::VariableCharacterType,
54
+ ].any? { |c| @data_type.is_a?(c) }
55
+ end
42
56
 
43
- def blob?
44
- @data_type.is_a? Innodb::DataType::BlobType
45
- end
57
+ def blob?
58
+ @data_type.is_a?(Innodb::DataType::BlobType)
59
+ end
46
60
 
47
- # Return the actual length of this variable-length field.
48
- def length(record)
49
- if record[:header][:lengths].include?(@name)
50
- len = record[:header][:lengths][@name]
51
- raise "Fixed-length mismatch" unless variable? || len == @data_type.width
52
- else
53
- len = @data_type.width
61
+ # Return the actual length of this variable-length field.
62
+ def length(record)
63
+ if record.header.lengths.include?(@name)
64
+ len = record.header.lengths[@name]
65
+ raise "Fixed-length mismatch" unless variable? || len == @data_type.width
66
+ else
67
+ len = @data_type.width
68
+ end
69
+ extern?(record) ? len - EXTERN_FIELD_SIZE : len
54
70
  end
55
- extern?(record) ? len - EXTERN_FIELD_SIZE : len
56
- end
57
71
 
58
- # Read an InnoDB encoded data field.
59
- def read(cursor, field_length)
60
- cursor.name(@data_type.name) { cursor.get_bytes(field_length) }
61
- end
72
+ # Read an InnoDB encoded data field.
73
+ def read(cursor, field_length)
74
+ cursor.name(@data_type.name) { cursor.read_bytes(field_length) }
75
+ end
62
76
 
63
- def value_by_length(cursor, field_length)
64
- if @data_type.respond_to?(:read)
65
- cursor.name(@data_type.name) { @data_type.read(cursor) }
66
- elsif @data_type.respond_to?(:value)
67
- @data_type.value(read(cursor, field_length))
68
- else
69
- read(cursor, field_length)
77
+ def value_by_length(cursor, field_length)
78
+ if @data_type.respond_to?(:read)
79
+ cursor.name(@data_type.name) { @data_type.read(cursor) }
80
+ elsif @data_type.respond_to?(:value)
81
+ @data_type.value(read(cursor, field_length))
82
+ else
83
+ read(cursor, field_length)
84
+ end
70
85
  end
71
- end
72
86
 
73
- # Read the data value (e.g. encoded in the data).
74
- def value(cursor, record)
75
- return :NULL if null?(record)
76
- value_by_length(cursor, length(record))
77
- end
87
+ # Read the data value (e.g. encoded in the data).
88
+ def value(cursor, record)
89
+ return :NULL if null?(record)
78
90
 
79
- # Read an InnoDB external pointer field.
80
- def extern(cursor, record)
81
- return nil if not extern?(record)
82
- cursor.name(@name) { read_extern(cursor) }
83
- end
91
+ value_by_length(cursor, length(record))
92
+ end
84
93
 
85
- private
86
-
87
- # Return an external reference field. An extern field contains the page
88
- # address and the length of the externally stored part of the record data.
89
- def get_extern_reference(cursor)
90
- {
91
- :space_id => cursor.name("space_id") { cursor.get_uint32 },
92
- :page_number => cursor.name("page_number") { cursor.get_uint32 },
93
- :offset => cursor.name("offset") { cursor.get_uint32 },
94
- :length => cursor.name("length") { cursor.get_uint64 & 0x3fffffff }
95
- }
96
- end
94
+ # Read an InnoDB external pointer field.
95
+ def extern(cursor, record)
96
+ return unless extern?(record)
97
97
 
98
- def read_extern(cursor)
99
- cursor.name("extern") { get_extern_reference(cursor) }
100
- end
98
+ cursor.name(@name) { read_extern(cursor) }
99
+ end
101
100
 
102
- # Parse a data type definition and extract the base type and any modifiers.
103
- def parse_type_definition(type_string)
104
- if matches = /^([a-zA-Z0-9_]+)(\(([0-9, ]+)\))?$/.match(type_string)
105
- base_type = matches[1].upcase.to_sym
106
- if matches[3]
107
- modifiers = matches[3].sub(/[ ]/, "").split(/,/).map { |s| s.to_i }
108
- else
109
- modifiers = []
101
+ private
102
+
103
+ # Return an external reference field. An extern field contains the page
104
+ # address and the length of the externally stored part of the record data.
105
+ def read_extern(cursor)
106
+ cursor.name("extern") do |c|
107
+ ExternReference.new(
108
+ space_id: c.name("space_id") { c.read_uint32 },
109
+ page_number: c.name("page_number") { c.read_uint32 },
110
+ offset: c.name("offset") { c.read_uint32 },
111
+ length: c.name("length") { c.read_uint64 & 0x3fffffff }
112
+ )
110
113
  end
111
- [base_type, modifiers]
114
+ end
115
+
116
+ # Parse a data type definition and extract the base type and any modifiers.
117
+ def parse_type_definition(type_string)
118
+ matches = /^([a-zA-Z0-9_]+)(\(([0-9, ]+)\))?$/.match(type_string)
119
+ return unless matches
120
+
121
+ base_type = matches[1].upcase.to_sym
122
+ return [base_type, []] unless matches[3]
123
+
124
+ [base_type, matches[3].sub(/ /, "").split(/,/).map(&:to_i)]
112
125
  end
113
126
  end
114
127
  end
@@ -1,36 +1,32 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # An InnoDB file segment entry, which appears in a few places, such as the
4
4
  # FSEG header of INDEX pages, and in the TRX_SYS pages.
5
5
 
6
- class Innodb::FsegEntry
7
- # The size (in bytes) of an FSEG entry, which contains a two 32-bit integers
8
- # and a 16-bit integer.
9
- SIZE = 4 + 4 + 2
6
+ module Innodb
7
+ class FsegEntry
8
+ # The size (in bytes) of an FSEG entry, which contains a two 32-bit integers
9
+ # and a 16-bit integer.
10
+ SIZE = 4 + 4 + 2
10
11
 
11
- # Return the FSEG entry address, which points to an entry on an INODE page.
12
- def self.get_entry_address(cursor)
13
- {
14
- :space_id => cursor.name("space_id") { cursor.get_uint32 },
15
- :page_number => cursor.name("page_number") {
16
- Innodb::Page.maybe_undefined(cursor.get_uint32)
17
- },
18
- :offset => cursor.name("offset") { cursor.get_uint16 },
19
- }
20
- end
21
-
22
- # Return an INODE entry which represents this file segment.
23
- def self.get_inode(space, cursor)
24
- address = cursor.name("address") { get_entry_address(cursor) }
25
- if address[:offset] == 0
26
- return nil
12
+ # Return the FSEG entry address, which points to an entry on an INODE page.
13
+ def self.get_entry_address(cursor)
14
+ {
15
+ space_id: cursor.name("space_id") { cursor.read_uint32 },
16
+ page_number: cursor.name("page_number") { Innodb::Page.maybe_undefined(cursor.read_uint32) },
17
+ offset: cursor.name("offset") { cursor.read_uint16 },
18
+ }
27
19
  end
28
20
 
29
- page = space.page(address[:page_number])
30
- if page.type != :INODE
31
- return nil
32
- end
21
+ # Return an INODE entry which represents this file segment.
22
+ def self.get_inode(space, cursor)
23
+ address = cursor.name("address") { get_entry_address(cursor) }
24
+ return nil if address[:offset].zero?
33
25
 
34
- page.inode_at(page.cursor(address[:offset]))
26
+ page = space.page(address[:page_number])
27
+ return nil unless page.type == :INODE
28
+
29
+ page.inode_at(page.cursor(address[:offset]))
30
+ end
35
31
  end
36
32
  end
@@ -1,30 +1,30 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # The global history of record versions implemented through undo logs.
4
- class Innodb::History
5
- def initialize(innodb_system)
6
- @innodb_system = innodb_system
7
- end
8
-
9
- # A helper to get to the trx_sys page in the Innodb::System.
10
- def trx_sys
11
- @innodb_system.system_space.trx_sys
12
- end
4
+ module Innodb
5
+ class History
6
+ def initialize(innodb_system)
7
+ @innodb_system = innodb_system
8
+ end
13
9
 
14
- # A helper to get to the history_list of a given space_id and page number.
15
- def history_list(space_id, page_number)
16
- @innodb_system.space(space_id).page(page_number).history_list
17
- end
10
+ # A helper to get to the trx_sys page in the Innodb::System.
11
+ def trx_sys
12
+ @innodb_system.system_space.trx_sys
13
+ end
18
14
 
19
- # Iterate through all history lists (one per rollback segment, nominally
20
- # there are 128 rollback segments).
21
- def each_history_list
22
- unless block_given?
23
- return enum_for(:each_history_list)
15
+ # A helper to get to the history_list of a given space_id and page number.
16
+ def history_list(space_id, page_number)
17
+ @innodb_system.space(space_id).page(page_number).history_list
24
18
  end
25
19
 
26
- trx_sys.rsegs.each do |slot|
27
- yield history_list(slot[:space_id], slot[:page_number])
20
+ # Iterate through all history lists (one per rollback segment, nominally
21
+ # there are 128 rollback segments).
22
+ def each_history_list
23
+ return enum_for(:each_history_list) unless block_given?
24
+
25
+ trx_sys.rsegs.each do |slot|
26
+ yield history_list(slot[:space_id], slot[:page_number])
27
+ end
28
28
  end
29
29
  end
30
30
  end
@@ -1,106 +1,102 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # A single history list; this is a more intelligent wrapper around the basic
4
4
  # Innodb::List::History which is provided elsewhere.
5
- class Innodb::HistoryList
6
- attr_reader :list
5
+ module Innodb
6
+ class HistoryList
7
+ attr_reader :list
7
8
 
8
- # Initialize from a provided Innodb::List::History.
9
- def initialize(list)
10
- @list = list
11
- end
12
-
13
- class UndoRecordCursor
14
- def initialize(history, undo_record, direction=:forward)
15
- @history = history
16
- @undo_record = undo_record
9
+ # Initialize from a provided Innodb::List::History.
10
+ def initialize(list)
11
+ @list = list
12
+ end
17
13
 
18
- case undo_record
19
- when :min
20
- @undo_log_cursor = history.list.list_cursor(:min, direction)
21
- if @undo_log = @undo_log_cursor.node
22
- @undo_record_cursor = @undo_log.undo_record_cursor(:min, direction)
23
- end
24
- when :max
25
- @undo_log_cursor = history.list.list_cursor(:max, direction)
26
- if @undo_log = @undo_log_cursor.node
27
- @undo_record_cursor = @undo_log.undo_record_cursor(:max, direction)
14
+ class UndoRecordCursor
15
+ def initialize(history, undo_record, direction = :forward)
16
+ @history = history
17
+ @undo_record = undo_record
18
+
19
+ # rubocop:disable Style/IfUnlessModifier
20
+ case undo_record
21
+ when :min
22
+ @undo_log_cursor = history.list.list_cursor(:min, direction)
23
+ if (@undo_log = @undo_log_cursor.node)
24
+ @undo_record_cursor = @undo_log.undo_record_cursor(:min, direction)
25
+ end
26
+ when :max
27
+ @undo_log_cursor = history.list.list_cursor(:max, direction)
28
+ if (@undo_log = @undo_log_cursor.node)
29
+ @undo_record_cursor = @undo_log.undo_record_cursor(:max, direction)
30
+ end
31
+ else
32
+ raise "Not implemented"
28
33
  end
29
- else
30
- raise "Not implemented"
34
+ # rubocop:enable Style/IfUnlessModifier
31
35
  end
32
- end
33
36
 
34
- def undo_record
35
- unless @undo_record_cursor
36
- return nil
37
- end
37
+ def undo_record
38
+ return nil unless @undo_record_cursor
38
39
 
39
- if rec = @undo_record_cursor.undo_record
40
- return rec
41
- end
40
+ if (rec = @undo_record_cursor.undo_record)
41
+ return rec
42
+ end
42
43
 
43
- case @direction
44
- when :forward
45
- next_undo_record
46
- when :backward
47
- prev_undo_record
44
+ case @direction
45
+ when :forward
46
+ next_undo_record
47
+ when :backward
48
+ prev_undo_record
49
+ end
48
50
  end
49
- end
50
51
 
51
- def move_cursor(page, undo_record)
52
- @undo_log = page
53
- @undo_log_cursor = @undo_log.undo_record_cursor(undo_record, @direction)
54
- end
55
-
56
- def next_undo_record
57
- if rec = @undo_record_cursor.undo_record
58
- return rec
52
+ def move_cursor(page, undo_record)
53
+ @undo_log = page
54
+ @undo_log_cursor = @undo_log.undo_record_cursor(undo_record, @direction)
59
55
  end
60
56
 
61
- if undo_log = @undo_log_cursor.node
62
- @undo_log = undo_log
63
- @undo_record_cursor = @undo_log.undo_record_cursor(:min, @direction)
64
- end
57
+ def next_undo_record
58
+ if (rec = @undo_record_cursor.undo_record)
59
+ return rec
60
+ end
65
61
 
66
- @undo_record_cursor.undo_record
67
- end
62
+ if (undo_log = @undo_log_cursor.node)
63
+ @undo_log = undo_log
64
+ @undo_record_cursor = @undo_log.undo_record_cursor(:min, @direction)
65
+ end
68
66
 
69
- def prev_undo_record
70
- if rec = @undo_log_cursor.undo_record
71
- return rec
67
+ @undo_record_cursor.undo_record
72
68
  end
73
69
 
74
- if undo_log = @undo_log_cursor.node
75
- @undo_log = undo_log
76
- @undo_record_cursor = @undo_log.undo_record_cursor(:max, @direction)
77
- end
70
+ def prev_undo_record
71
+ if (rec = @undo_log_cursor.undo_record)
72
+ return rec
73
+ end
78
74
 
79
- @undo_record_cursor.undo_record
80
- end
75
+ if (undo_log = @undo_log_cursor.node)
76
+ @undo_log = undo_log
77
+ @undo_record_cursor = @undo_log.undo_record_cursor(:max, @direction)
78
+ end
81
79
 
82
- def each_undo_record
83
- unless block_given?
84
- return enum_for(:each_undo_record)
80
+ @undo_record_cursor.undo_record
85
81
  end
86
82
 
87
- while rec = undo_record
88
- yield rec
83
+ def each_undo_record
84
+ return enum_for(:each_undo_record) unless block_given?
85
+
86
+ while (rec = undo_record)
87
+ yield rec
88
+ end
89
89
  end
90
90
  end
91
- end
92
91
 
93
- def undo_record_cursor(undo_record=:min, direction=:forward)
94
- UndoRecordCursor.new(self, undo_record, direction)
95
- end
96
-
97
- def each_undo_record
98
- unless block_given?
99
- return enum_for(:each_undo_record)
92
+ def undo_record_cursor(undo_record = :min, direction = :forward)
93
+ UndoRecordCursor.new(self, undo_record, direction)
100
94
  end
101
95
 
102
- undo_record_cursor.each_undo_record do |rec|
103
- yield rec
96
+ def each_undo_record(&block)
97
+ return enum_for(:each_undo_record) unless block_given?
98
+
99
+ undo_record_cursor.each_undo_record(&block)
104
100
  end
105
101
  end
106
102
  end