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