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.rb CHANGED
@@ -1,21 +1,20 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # A set of classes for parsing and working with InnoDB data files.
4
4
 
5
5
  module Innodb
6
- @@debug = false
6
+ @debug = false
7
7
 
8
8
  def self.debug?
9
- @@debug == true
9
+ @debug == true
10
10
  end
11
11
 
12
12
  def self.debug=(value)
13
- @@debug = value
13
+ @debug = value
14
14
  end
15
15
  end
16
16
 
17
17
  require "pp"
18
- require "enumerator"
19
18
  require "digest/crc32c"
20
19
  require "innodb/util/buffer_cursor"
21
20
  require "innodb/util/read_bits_at_offset"
@@ -1,31 +1,33 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- class Innodb::Checksum
4
- MAX = 0xFFFFFFFF.freeze
5
- MASK1 = 1463735687.freeze
6
- MASK2 = 1653893711.freeze
3
+ module Innodb
4
+ class Checksum
5
+ MAX = 0xFFFFFFFF
6
+ MASK1 = 1_463_735_687
7
+ MASK2 = 1_653_893_711
7
8
 
8
- # This is derived from ut_fold_ulint_pair in include/ut0rnd.ic in the
9
- # InnoDB source code. Since Ruby's Bignum class is *much* slower than its
10
- # Integer class, we mask back to 32 bits to keep things from overflowing
11
- # and being promoted to Bignum.
12
- def self.fold_pair(n1, n2)
13
- (((((((n1 ^ n2 ^ MASK2) << 8) & MAX) + n1) & MAX) ^ MASK1) + n2) & MAX
14
- end
9
+ # This is derived from ut_fold_ulint_pair in include/ut0rnd.ic in the
10
+ # InnoDB source code. Since Ruby's Bignum class is *much* slower than its
11
+ # Integer class, we mask back to 32 bits to keep things from overflowing
12
+ # and being promoted to Bignum.
13
+ def self.fold_pair(num1, num2)
14
+ (((((((num1 ^ num2 ^ MASK2) << 8) & MAX) + num1) & MAX) ^ MASK1) + num2) & MAX
15
+ end
15
16
 
16
- # Iterate through the provided enumerator, which is expected to return a
17
- # Integer (or something coercible to it), and "fold" them together to produce
18
- # a single value.
19
- def self.fold_enumerator(enumerator)
20
- fold = 0
21
- enumerator.each do |byte|
22
- fold = fold_pair(fold, byte)
17
+ # Iterate through the provided enumerator, which is expected to return a
18
+ # Integer (or something coercible to it), and "fold" them together to produce
19
+ # a single value.
20
+ def self.fold_enumerator(enumerator)
21
+ fold = 0
22
+ enumerator.each do |byte|
23
+ fold = fold_pair(fold, byte)
24
+ end
25
+ fold
23
26
  end
24
- fold
25
- end
26
27
 
27
- # A simple helper (and example) to fold a provided string.
28
- def self.fold_string(string)
29
- fold_enumerator(string.bytes)
28
+ # A simple helper (and example) to fold a provided string.
29
+ def self.fold_string(string)
30
+ fold_enumerator(string.bytes)
31
+ end
30
32
  end
31
33
  end
@@ -1,689 +1,629 @@
1
- # -*- encoding : utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  # A class representing InnoDB's data dictionary, which contains metadata about
4
4
  # tables, columns, and indexes.
5
- class Innodb::DataDictionary
6
- # A record describer for SYS_TABLES clustered records.
7
- class SYS_TABLES_PRIMARY < Innodb::RecordDescriber
8
- type :clustered
9
- key "NAME", "VARCHAR(100)", :NOT_NULL
10
- row "ID", :BIGINT, :UNSIGNED, :NOT_NULL
11
- row "N_COLS", :INT, :UNSIGNED, :NOT_NULL
12
- row "TYPE", :INT, :UNSIGNED, :NOT_NULL
13
- row "MIX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
14
- row "MIX_LEN", :INT, :UNSIGNED, :NOT_NULL
15
- row "CLUSTER_NAME", "VARCHAR(100)", :NOT_NULL
16
- row "SPACE", :INT, :UNSIGNED, :NOT_NULL
17
- end
18
-
19
- # A record describer for SYS_TABLES secondary key on ID.
20
- class SYS_TABLES_ID < Innodb::RecordDescriber
21
- type :secondary
22
- key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
23
- row "NAME", "VARCHAR(100)", :NOT_NULL
24
- end
25
-
26
- # A record describer for SYS_COLUMNS clustered records.
27
- class SYS_COLUMNS_PRIMARY < Innodb::RecordDescriber
28
- type :clustered
29
- key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
30
- key "POS", :INT, :UNSIGNED, :NOT_NULL
31
- row "NAME", "VARCHAR(100)", :NOT_NULL
32
- row "MTYPE", :INT, :UNSIGNED, :NOT_NULL
33
- row "PRTYPE", :INT, :UNSIGNED, :NOT_NULL
34
- row "LEN", :INT, :UNSIGNED, :NOT_NULL
35
- row "PREC", :INT, :UNSIGNED, :NOT_NULL
36
- end
37
-
38
- # A record describer for SYS_INDEXES clustered records.
39
- class SYS_INDEXES_PRIMARY < Innodb::RecordDescriber
40
- type :clustered
41
- key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
42
- key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
43
- row "NAME", "VARCHAR(100)", :NOT_NULL
44
- row "N_FIELDS", :INT, :UNSIGNED, :NOT_NULL
45
- row "TYPE", :INT, :UNSIGNED, :NOT_NULL
46
- row "SPACE", :INT, :UNSIGNED, :NOT_NULL
47
- row "PAGE_NO", :INT, :UNSIGNED, :NOT_NULL
48
- end
49
-
50
- # A record describer for SYS_FIELDS clustered records.
51
- class SYS_FIELDS_PRIMARY < Innodb::RecordDescriber
52
- type :clustered
53
- key "INDEX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
54
- key "POS", :INT, :UNSIGNED, :NOT_NULL
55
- row "COL_NAME", "VARCHAR(100)", :NOT_NULL
56
- end
57
-
58
- # A hash of hashes of table name and index name to describer
59
- # class.
60
- DATA_DICTIONARY_RECORD_DESCRIBERS = {
61
- :SYS_TABLES => {
62
- :PRIMARY => SYS_TABLES_PRIMARY,
63
- :ID => SYS_TABLES_ID
64
- },
65
- :SYS_COLUMNS => { :PRIMARY => SYS_COLUMNS_PRIMARY },
66
- :SYS_INDEXES => { :PRIMARY => SYS_INDEXES_PRIMARY },
67
- :SYS_FIELDS => { :PRIMARY => SYS_FIELDS_PRIMARY },
68
- }
69
-
70
- # A hash of MySQL's internal type system to the stored
71
- # values for those types, and the "external" SQL type.
72
- MYSQL_TYPE = {
73
- # :DECIMAL => { :value => 0, :type => :DECIMAL },
74
- :TINY => { :value => 1, :type => :TINYINT },
75
- :SHORT => { :value => 2, :type => :SMALLINT },
76
- :LONG => { :value => 3, :type => :INT },
77
- :FLOAT => { :value => 4, :type => :FLOAT },
78
- :DOUBLE => { :value => 5, :type => :DOUBLE },
79
- # :NULL => { :value => 6, :type => nil },
80
- :TIMESTAMP => { :value => 7, :type => :TIMESTAMP },
81
- :LONGLONG => { :value => 8, :type => :BIGINT },
82
- :INT24 => { :value => 9, :type => :MEDIUMINT },
83
- # :DATE => { :value => 10, :type => :DATE },
84
- :TIME => { :value => 11, :type => :TIME },
85
- :DATETIME => { :value => 12, :type => :DATETIME },
86
- :YEAR => { :value => 13, :type => :YEAR },
87
- :NEWDATE => { :value => 14, :type => :DATE },
88
- :VARCHAR => { :value => 15, :type => :VARCHAR },
89
- :BIT => { :value => 16, :type => :BIT },
90
- :NEWDECIMAL => { :value => 246, :type => :CHAR },
91
- # :ENUM => { :value => 247, :type => :ENUM },
92
- # :SET => { :value => 248, :type => :SET },
93
- :TINY_BLOB => { :value => 249, :type => :TINYBLOB },
94
- :MEDIUM_BLOB => { :value => 250, :type => :MEDIUMBLOB },
95
- :LONG_BLOB => { :value => 251, :type => :LONGBLOB },
96
- :BLOB => { :value => 252, :type => :BLOB },
97
- # :VAR_STRING => { :value => 253, :type => :VARCHAR },
98
- :STRING => { :value => 254, :type => :CHAR },
99
- :GEOMETRY => { :value => 255, :type => :GEOMETRY },
100
- }
101
-
102
- # A hash of MYSQL_TYPE keys by value :value key.
103
- MYSQL_TYPE_BY_VALUE = MYSQL_TYPE.inject({}) { |h, (k, v)| h[v[:value]] = k; h }
104
-
105
- # A hash of InnoDB's internal type system to the values
106
- # stored for each type.
107
- COLUMN_MTYPE = {
108
- :VARCHAR => 1,
109
- :CHAR => 2,
110
- :FIXBINARY => 3,
111
- :BINARY => 4,
112
- :BLOB => 5,
113
- :INT => 6,
114
- :SYS_CHILD => 7,
115
- :SYS => 8,
116
- :FLOAT => 9,
117
- :DOUBLE => 10,
118
- :DECIMAL => 11,
119
- :VARMYSQL => 12,
120
- :MYSQL => 13,
121
- }
122
-
123
- # A hash of COLUMN_MTYPE keys by value.
124
- COLUMN_MTYPE_BY_VALUE = COLUMN_MTYPE.inject({}) { |h, (k, v)| h[v] = k; h }
125
-
126
- # A hash of InnoDB "precise type" bitwise flags.
127
- COLUMN_PRTYPE_FLAG = {
128
- :NOT_NULL => 256,
129
- :UNSIGNED => 512,
130
- :BINARY => 1024,
131
- :LONG_TRUE_VARCHAR => 4096,
132
- }
133
-
134
- # A hash of COLUMN_PRTYPE keys by value.
135
- COLUMN_PRTYPE_FLAG_BY_VALUE = COLUMN_PRTYPE_FLAG.inject({}) { |h, (k, v)| h[v] = k; h }
136
-
137
- # The bitmask to extract the MySQL internal type
138
- # from the InnoDB "precise type".
139
- COLUMN_PRTYPE_MYSQL_TYPE_MASK = 0xFF
140
-
141
- # A hash of InnoDB's index type flags.
142
- INDEX_TYPE_FLAG = {
143
- :CLUSTERED => 1,
144
- :UNIQUE => 2,
145
- :UNIVERSAL => 4,
146
- :IBUF => 8,
147
- :CORRUPT => 16,
148
- :FTS => 32,
149
- }
150
-
151
- # A hash of INDEX_TYPE_FLAG keys by value.
152
- INDEX_TYPE_FLAG_BY_VALUE = INDEX_TYPE_FLAG.inject({}) { |h, (k, v)| h[v] = k; h }
153
-
154
- # Return the "external" SQL type string (such as "VARCHAR" or
155
- # "INT") given the stored mtype and prtype from the InnoDB
156
- # data dictionary. Note that not all types are extractable
157
- # into fully defined SQL types due to the lossy nature of
158
- # the MySQL-to-InnoDB interface regarding types.
159
- def self.mtype_prtype_to_type_string(mtype, prtype, len, prec)
160
- mysql_type = prtype & COLUMN_PRTYPE_MYSQL_TYPE_MASK
161
- internal_type = MYSQL_TYPE_BY_VALUE[mysql_type]
162
- external_type = MYSQL_TYPE[internal_type][:type]
163
-
164
- case external_type
165
- when :VARCHAR
166
- # One-argument: length.
167
- "%s(%i)" % [external_type, len]
168
- when :FLOAT, :DOUBLE
169
- # Two-argument: length and precision.
170
- "%s(%i,%i)" % [external_type, len, prec]
171
- when :CHAR
172
- if COLUMN_MTYPE_BY_VALUE[mtype] == :MYSQL
173
- # When the mtype is :MYSQL, the column is actually
174
- # stored as VARCHAR despite being a CHAR. This is
175
- # done for CHAR columns having multi-byte character
176
- # sets in order to limit size. Note that such data
177
- # are still space-padded to at least len.
178
- "VARCHAR(%i)" % [len]
179
- else
5
+ module Innodb
6
+ class DataDictionary
7
+ MysqlType = Struct.new(
8
+ :value,
9
+ :type,
10
+ keyword_init: true
11
+ )
12
+
13
+ # rubocop:disable Layout/ExtraSpacing
14
+
15
+ # A record describer for SYS_TABLES clustered records.
16
+ class SysTablesPrimary < Innodb::RecordDescriber
17
+ type :clustered
18
+ key "NAME", "VARCHAR(100)", :NOT_NULL
19
+ row "ID", :BIGINT, :UNSIGNED, :NOT_NULL
20
+ row "N_COLS", :INT, :UNSIGNED, :NOT_NULL
21
+ row "TYPE", :INT, :UNSIGNED, :NOT_NULL
22
+ row "MIX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
23
+ row "MIX_LEN", :INT, :UNSIGNED, :NOT_NULL
24
+ row "CLUSTER_NAME", "VARCHAR(100)", :NOT_NULL
25
+ row "SPACE", :INT, :UNSIGNED, :NOT_NULL
26
+ end
27
+
28
+ # A record describer for SYS_TABLES secondary key on ID.
29
+ class SysTablesId < Innodb::RecordDescriber
30
+ type :secondary
31
+ key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
32
+ row "NAME", "VARCHAR(100)", :NOT_NULL
33
+ end
34
+
35
+ # A record describer for SYS_COLUMNS clustered records.
36
+ class SysColumnsPrimary < Innodb::RecordDescriber
37
+ type :clustered
38
+ key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
39
+ key "POS", :INT, :UNSIGNED, :NOT_NULL
40
+ row "NAME", "VARCHAR(100)", :NOT_NULL
41
+ row "MTYPE", :INT, :UNSIGNED, :NOT_NULL
42
+ row "PRTYPE", :INT, :UNSIGNED, :NOT_NULL
43
+ row "LEN", :INT, :UNSIGNED, :NOT_NULL
44
+ row "PREC", :INT, :UNSIGNED, :NOT_NULL
45
+ end
46
+
47
+ # A record describer for SYS_INDEXES clustered records.
48
+ class SysIndexesPrimary < Innodb::RecordDescriber
49
+ type :clustered
50
+ key "TABLE_ID", :BIGINT, :UNSIGNED, :NOT_NULL
51
+ key "ID", :BIGINT, :UNSIGNED, :NOT_NULL
52
+ row "NAME", "VARCHAR(100)", :NOT_NULL
53
+ row "N_FIELDS", :INT, :UNSIGNED, :NOT_NULL
54
+ row "TYPE", :INT, :UNSIGNED, :NOT_NULL
55
+ row "SPACE", :INT, :UNSIGNED, :NOT_NULL
56
+ row "PAGE_NO", :INT, :UNSIGNED, :NOT_NULL
57
+ end
58
+
59
+ # A record describer for SYS_FIELDS clustered records.
60
+ class SysFieldsPrimary < Innodb::RecordDescriber
61
+ type :clustered
62
+ key "INDEX_ID", :BIGINT, :UNSIGNED, :NOT_NULL
63
+ key "POS", :INT, :UNSIGNED, :NOT_NULL
64
+ row "COL_NAME", "VARCHAR(100)", :NOT_NULL
65
+ end
66
+
67
+ # rubocop:enable Layout/ExtraSpacing
68
+
69
+ # A hash of hashes of table name and index name to describer
70
+ # class.
71
+ DATA_DICTIONARY_RECORD_DESCRIBERS = {
72
+ SYS_TABLES: {
73
+ PRIMARY: SysTablesPrimary,
74
+ ID: SysTablesId,
75
+ }.freeze,
76
+ SYS_COLUMNS: { PRIMARY: SysColumnsPrimary }.freeze,
77
+ SYS_INDEXES: { PRIMARY: SysIndexesPrimary }.freeze,
78
+ SYS_FIELDS: { PRIMARY: SysFieldsPrimary }.freeze,
79
+ }.freeze
80
+
81
+ # A hash of MySQL's internal type system to the stored
82
+ # values for those types, and the 'external' SQL type.
83
+ # rubocop:disable Layout/HashAlignment
84
+ # rubocop:disable Layout/CommentIndentation
85
+ MYSQL_TYPE = {
86
+ # DECIMAL: MysqlType.new(value: 0, type: :DECIMAL),
87
+ TINY: MysqlType.new(value: 1, type: :TINYINT),
88
+ SHORT: MysqlType.new(value: 2, type: :SMALLINT),
89
+ LONG: MysqlType.new(value: 3, type: :INT),
90
+ FLOAT: MysqlType.new(value: 4, type: :FLOAT),
91
+ DOUBLE: MysqlType.new(value: 5, type: :DOUBLE),
92
+ # NULL: MysqlType.new(value: 6, type: nil),
93
+ TIMESTAMP: MysqlType.new(value: 7, type: :TIMESTAMP),
94
+ LONGLONG: MysqlType.new(value: 8, type: :BIGINT),
95
+ INT24: MysqlType.new(value: 9, type: :MEDIUMINT),
96
+ # DATE: MysqlType.new(value: 10, type: :DATE),
97
+ TIME: MysqlType.new(value: 11, type: :TIME),
98
+ DATETIME: MysqlType.new(value: 12, type: :DATETIME),
99
+ YEAR: MysqlType.new(value: 13, type: :YEAR),
100
+ NEWDATE: MysqlType.new(value: 14, type: :DATE),
101
+ VARCHAR: MysqlType.new(value: 15, type: :VARCHAR),
102
+ BIT: MysqlType.new(value: 16, type: :BIT),
103
+ NEWDECIMAL: MysqlType.new(value: 246, type: :CHAR),
104
+ # ENUM: MysqlType.new(value: 247, type: :ENUM),
105
+ # SET: MysqlType.new(value: 248, type: :SET),
106
+ TINY_BLOB: MysqlType.new(value: 249, type: :TINYBLOB),
107
+ MEDIUM_BLOB: MysqlType.new(value: 250, type: :MEDIUMBLOB),
108
+ LONG_BLOB: MysqlType.new(value: 251, type: :LONGBLOB),
109
+ BLOB: MysqlType.new(value: 252, type: :BLOB),
110
+ # VAR_STRING: MysqlType.new(value: 253, type: :VARCHAR),
111
+ STRING: MysqlType.new(value: 254, type: :CHAR),
112
+ GEOMETRY: MysqlType.new(value: 255, type: :GEOMETRY),
113
+ }.freeze
114
+ # rubocop:enable Layout/CommentIndentation
115
+ # rubocop:enable Layout/HashAlignment
116
+
117
+ # A hash of MYSQL_TYPE keys by value :value key.
118
+ MYSQL_TYPE_BY_VALUE = MYSQL_TYPE.transform_values(&:value).invert.freeze
119
+
120
+ # A hash of InnoDB's internal type system to the values
121
+ # stored for each type.
122
+ COLUMN_MTYPE = {
123
+ VARCHAR: 1,
124
+ CHAR: 2,
125
+ FIXBINARY: 3,
126
+ BINARY: 4,
127
+ BLOB: 5,
128
+ INT: 6,
129
+ SYS_CHILD: 7,
130
+ SYS: 8,
131
+ FLOAT: 9,
132
+ DOUBLE: 10,
133
+ DECIMAL: 11,
134
+ VARMYSQL: 12,
135
+ MYSQL: 13,
136
+ }.freeze
137
+
138
+ # A hash of COLUMN_MTYPE keys by value.
139
+ COLUMN_MTYPE_BY_VALUE = COLUMN_MTYPE.invert.freeze
140
+
141
+ # A hash of InnoDB 'precise type' bitwise flags.
142
+ COLUMN_PRTYPE_FLAG = {
143
+ NOT_NULL: 256,
144
+ UNSIGNED: 512,
145
+ BINARY: 1024,
146
+ LONG_TRUE_VARCHAR: 4096,
147
+ }.freeze
148
+
149
+ # A hash of COLUMN_PRTYPE keys by value.
150
+ COLUMN_PRTYPE_FLAG_BY_VALUE = COLUMN_PRTYPE_FLAG.invert.freeze
151
+
152
+ # The bitmask to extract the MySQL internal type
153
+ # from the InnoDB 'precise type'.
154
+ COLUMN_PRTYPE_MYSQL_TYPE_MASK = 0xFF
155
+
156
+ # A hash of InnoDB's index type flags.
157
+ INDEX_TYPE_FLAG = {
158
+ CLUSTERED: 1,
159
+ UNIQUE: 2,
160
+ UNIVERSAL: 4,
161
+ IBUF: 8,
162
+ CORRUPT: 16,
163
+ FTS: 32,
164
+ }.freeze
165
+
166
+ # A hash of INDEX_TYPE_FLAG keys by value.
167
+ INDEX_TYPE_FLAG_BY_VALUE = INDEX_TYPE_FLAG.invert.freeze
168
+
169
+ # Return the 'external' SQL type string (such as 'VARCHAR' or
170
+ # 'INT') given the stored mtype and prtype from the InnoDB
171
+ # data dictionary. Note that not all types are extractable
172
+ # into fully defined SQL types due to the lossy nature of
173
+ # the MySQL-to-InnoDB interface regarding types.
174
+ def self.mtype_prtype_to_type_string(mtype, prtype, len, prec)
175
+ mysql_type = prtype & COLUMN_PRTYPE_MYSQL_TYPE_MASK
176
+ internal_type = MYSQL_TYPE_BY_VALUE[mysql_type]
177
+ external_type = MYSQL_TYPE[internal_type].type
178
+
179
+ case external_type
180
+ when :VARCHAR
181
+ # One-argument: length.
182
+ "%s(%i)" % [external_type, len]
183
+ when :FLOAT, :DOUBLE
184
+ # Two-argument: length and precision.
185
+ "%s(%i,%i)" % [external_type, len, prec]
186
+ when :CHAR
187
+ if COLUMN_MTYPE_BY_VALUE[mtype] == :MYSQL
188
+ # When the mtype is :MYSQL, the column is actually
189
+ # stored as VARCHAR despite being a CHAR. This is
190
+ # done for CHAR columns having multi-byte character
191
+ # sets in order to limit size. Note that such data
192
+ # are still space-padded to at least len.
193
+ "VARCHAR(%i)" % [len]
194
+ else
195
+ "CHAR(%i)" % [len]
196
+ end
197
+ when :DECIMAL
198
+ # The DECIMAL type is designated as DECIMAL(M,D)
199
+ # however the M and D definitions are not stored
200
+ # in the InnoDB data dictionary. We need to define
201
+ # the column as something which will extract the
202
+ # raw bytes in order to read the column, but we
203
+ # can't figure out the right decimal type. The
204
+ # len stored here is actually the on-disk storage
205
+ # size.
180
206
  "CHAR(%i)" % [len]
207
+ else
208
+ external_type
181
209
  end
182
- when :DECIMAL
183
- # The DECIMAL type is designated as DECIMAL(M,D)
184
- # however the M and D definitions are not stored
185
- # in the InnoDB data dictionary. We need to define
186
- # the column as something which will extract the
187
- # raw bytes in order to read the column, but we
188
- # can't figure out the right decimal type. The
189
- # len stored here is actually the on-disk storage
190
- # size.
191
- "CHAR(%i)" % [len]
192
- else
193
- external_type
194
210
  end
195
- end
196
211
 
197
- # Return a full data type given an mtype and prtype, such
198
- # as ["VARCHAR(10)", :NOT_NULL] or [:INT, :UNSIGNED].
199
- def self.mtype_prtype_to_data_type(mtype, prtype, len, prec)
200
- data_type = []
212
+ # Return a full data type given an mtype and prtype, such
213
+ # as ['VARCHAR(10)', :NOT_NULL] or [:INT, :UNSIGNED].
214
+ def self.mtype_prtype_to_data_type(mtype, prtype, len, prec)
215
+ type = mtype_prtype_to_type_string(mtype, prtype, len, prec)
216
+ raise "Unsupported type (mtype #{mtype}, prtype #{prtype})" unless type
201
217
 
202
- if type = mtype_prtype_to_type_string(mtype, prtype, len, prec)
203
- data_type << type
204
- else
205
- raise "Unsupported type (mtype #{mtype}, prtype #{prtype})"
206
- end
218
+ data_type = [type]
219
+ data_type << :NOT_NULL if prtype & COLUMN_PRTYPE_FLAG[:NOT_NULL] != 0
220
+ data_type << :UNSIGNED if prtype & COLUMN_PRTYPE_FLAG[:UNSIGNED] != 0
207
221
 
208
- if prtype & COLUMN_PRTYPE_FLAG[:NOT_NULL] != 0
209
- data_type << :NOT_NULL
222
+ data_type
210
223
  end
211
224
 
212
- if prtype & COLUMN_PRTYPE_FLAG[:UNSIGNED] != 0
213
- data_type << :UNSIGNED
214
- end
225
+ attr_reader :system_space
215
226
 
216
- data_type
217
- end
218
-
219
- attr_reader :system_space
227
+ def initialize(system_space)
228
+ @system_space = system_space
229
+ end
220
230
 
221
- def initialize(system_space)
222
- @system_space = system_space
223
- end
231
+ # A helper method to reach inside the system space and retrieve
232
+ # the data dictionary index locations from the data dictionary
233
+ # header.
234
+ def data_dictionary_indexes
235
+ system_space.data_dictionary_page.data_dictionary_header[:indexes]
236
+ end
224
237
 
225
- # A helper method to reach inside the system space and retrieve
226
- # the data dictionary index locations from the data dictionary
227
- # header.
228
- def data_dictionary_indexes
229
- system_space.data_dictionary_page.data_dictionary_header[:indexes]
230
- end
238
+ def data_dictionary_index_ids
239
+ return @data_dictionary_index_ids if @data_dictionary_index_ids
231
240
 
232
- def data_dictionary_index_ids
233
- if @data_dictionary_index_ids
234
- return @data_dictionary_index_ids
235
- end
241
+ # TODO: This could probably be done a lot more Ruby-like.
242
+ @data_dictionary_index_ids = {}
243
+ data_dictionary_indexes.each do |table, indexes|
244
+ indexes.each do |index, root_page_number|
245
+ root_page = system_space.page(root_page_number)
246
+ next unless root_page
236
247
 
237
- @data_dictionary_index_ids = {}
238
- data_dictionary_indexes.each do |table, indexes|
239
- indexes.each do |index, root_page_number|
240
- if root_page = system_space.page(root_page_number)
241
248
  @data_dictionary_index_ids[root_page.index_id] = {
242
- :table => table,
243
- :index => index
249
+ table: table,
250
+ index: index,
244
251
  }
245
252
  end
246
253
  end
247
- end
248
254
 
249
- @data_dictionary_index_ids
250
- end
255
+ @data_dictionary_index_ids
256
+ end
251
257
 
252
- def data_dictionary_table?(table_name)
253
- DATA_DICTIONARY_RECORD_DESCRIBERS.include?(table_name.to_sym)
254
- end
258
+ def data_dictionary_table?(table_name)
259
+ DATA_DICTIONARY_RECORD_DESCRIBERS.include?(table_name.to_sym)
260
+ end
255
261
 
256
- def data_dictionary_index?(table_name, index_name)
257
- return unless data_dictionary_table?(table_name)
258
- DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym].include?(index_name.to_sym)
259
- end
262
+ def data_dictionary_index?(table_name, index_name)
263
+ return unless data_dictionary_table?(table_name)
260
264
 
261
- def data_dictionary_index_describer(table_name, index_name)
262
- return unless data_dictionary_index?(table_name, index_name)
265
+ DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym].include?(index_name.to_sym)
266
+ end
263
267
 
264
- DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym][index_name.to_sym].new
265
- end
268
+ def data_dictionary_index_describer(table_name, index_name)
269
+ return unless data_dictionary_index?(table_name, index_name)
266
270
 
267
- # Return an Innodb::Index object initialized to the
268
- # internal data dictionary index with an appropriate
269
- # record describer so that records can be recursed.
270
- def data_dictionary_index(table_name, index_name)
271
- unless table_entry = data_dictionary_indexes[table_name]
272
- raise "Unknown data dictionary table #{table_name}"
271
+ DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym][index_name.to_sym].new
273
272
  end
274
273
 
275
- unless index_root_page = table_entry[index_name]
276
- raise "Unknown data dictionary index #{table_name}.#{index_name}"
277
- end
274
+ # Return an Innodb::Index object initialized to the
275
+ # internal data dictionary index with an appropriate
276
+ # record describer so that records can be recursed.
277
+ def data_dictionary_index(table_name, index_name)
278
+ table_entry = data_dictionary_indexes[table_name]
279
+ raise "Unknown data dictionary table #{table_name}" unless table_entry
278
280
 
279
- # If we have a record describer for this index, load it.
280
- record_describer = data_dictionary_index_describer(table_name, index_name)
281
+ index_root_page = table_entry[index_name]
282
+ raise "Unknown data dictionary index #{table_name}.#{index_name}" unless index_root_page
281
283
 
282
- system_space.index(index_root_page, record_describer)
283
- end
284
+ # If we have a record describer for this index, load it.
285
+ record_describer = data_dictionary_index_describer(table_name, index_name)
284
286
 
285
- # Iterate through all data dictionary indexes, yielding the
286
- # table name, index name, and root page number.
287
- def each_data_dictionary_index_root_page_number
288
- unless block_given?
289
- return enum_for(:each_data_dictionary_index_root_page_number)
287
+ system_space.index(index_root_page, record_describer)
290
288
  end
291
289
 
292
- data_dictionary_indexes.each do |table_name, indexes|
293
- indexes.each do |index_name, root_page_number|
294
- yield table_name, index_name, root_page_number
295
- end
296
- end
290
+ # Iterate through all data dictionary indexes, yielding the
291
+ # table name, index name, and root page number.
292
+ def each_data_dictionary_index_root_page_number
293
+ return enum_for(:each_data_dictionary_index_root_page_number) unless block_given?
297
294
 
298
- nil
299
- end
295
+ data_dictionary_indexes.each do |table_name, indexes|
296
+ indexes.each do |index_name, root_page_number|
297
+ yield table_name, index_name, root_page_number
298
+ end
299
+ end
300
300
 
301
- # Iterate through all data dictionary indexes, yielding the table
302
- # name, index name, and the index itself as an Innodb::Index.
303
- def each_data_dictionary_index
304
- unless block_given?
305
- return enum_for(:each_data_dictionary_index)
301
+ nil
306
302
  end
307
303
 
308
- data_dictionary_indexes.each do |table_name, indexes|
309
- indexes.each do |index_name, root_page_number|
310
- yield table_name, index_name,
311
- data_dictionary_index(table_name, index_name)
312
- end
313
- end
304
+ # Iterate through all data dictionary indexes, yielding the table
305
+ # name, index name, and the index itself as an Innodb::Index.
306
+ def each_data_dictionary_index
307
+ return enum_for(:each_data_dictionary_index) unless block_given?
314
308
 
315
- nil
316
- end
309
+ data_dictionary_indexes.each do |table_name, indexes|
310
+ indexes.each do |index_name, _root_page_number|
311
+ yield table_name, index_name,
312
+ data_dictionary_index(table_name, index_name)
313
+ end
314
+ end
317
315
 
318
- # Iterate through records from a data dictionary index yielding each record
319
- # as a Innodb::Record object.
320
- def each_record_from_data_dictionary_index(table, index)
321
- unless block_given?
322
- return enum_for(:each_index, table, index)
316
+ nil
323
317
  end
324
318
 
325
- data_dictionary_index(table, index).each_record do |record|
326
- yield record
327
- end
319
+ # Iterate through records from a data dictionary index yielding each record
320
+ # as a Innodb::Record object.
321
+ def each_record_from_data_dictionary_index(table, index, &block)
322
+ return enum_for(:each_record_from_data_dictionary_index, table, index) unless block_given?
328
323
 
329
- nil
330
- end
324
+ data_dictionary_index(table, index).each_record(&block)
331
325
 
332
- # Iterate through the records in the SYS_TABLES data dictionary table.
333
- def each_table
334
- unless block_given?
335
- return enum_for(:each_table)
326
+ nil
336
327
  end
337
328
 
338
- each_record_from_data_dictionary_index(:SYS_TABLES, :PRIMARY) do |record|
339
- yield record.fields
340
- end
329
+ # Iterate through the records in the SYS_TABLES data dictionary table.
330
+ def each_table
331
+ return enum_for(:each_table) unless block_given?
341
332
 
342
- nil
343
- end
333
+ each_record_from_data_dictionary_index(:SYS_TABLES, :PRIMARY) do |record|
334
+ yield record.fields
335
+ end
344
336
 
345
- # Iterate through the records in the SYS_COLUMNS data dictionary table.
346
- def each_column
347
- unless block_given?
348
- return enum_for(:each_column)
337
+ nil
349
338
  end
350
339
 
351
- each_record_from_data_dictionary_index(:SYS_COLUMNS, :PRIMARY) do |record|
352
- yield record.fields
353
- end
340
+ # Iterate through the records in the SYS_COLUMNS data dictionary table.
341
+ def each_column
342
+ return enum_for(:each_column) unless block_given?
354
343
 
355
- nil
356
- end
344
+ each_record_from_data_dictionary_index(:SYS_COLUMNS, :PRIMARY) do |record|
345
+ yield record.fields
346
+ end
357
347
 
358
- # Iterate through the records in the SYS_INDEXES dictionary table.
359
- def each_index
360
- unless block_given?
361
- return enum_for(:each_index)
348
+ nil
362
349
  end
363
350
 
364
- each_record_from_data_dictionary_index(:SYS_INDEXES, :PRIMARY) do |record|
365
- yield record.fields
366
- end
351
+ # Iterate through the records in the SYS_INDEXES dictionary table.
352
+ def each_index
353
+ return enum_for(:each_index) unless block_given?
367
354
 
368
- nil
369
- end
355
+ each_record_from_data_dictionary_index(:SYS_INDEXES, :PRIMARY) do |record|
356
+ yield record.fields
357
+ end
370
358
 
371
- # Iterate through the records in the SYS_FIELDS data dictionary table.
372
- def each_field
373
- unless block_given?
374
- return enum_for(:each_field)
359
+ nil
375
360
  end
376
361
 
377
- each_record_from_data_dictionary_index(:SYS_FIELDS, :PRIMARY) do |record|
378
- yield record.fields
379
- end
362
+ # Iterate through the records in the SYS_FIELDS data dictionary table.
363
+ def each_field
364
+ return enum_for(:each_field) unless block_given?
380
365
 
381
- nil
382
- end
366
+ each_record_from_data_dictionary_index(:SYS_FIELDS, :PRIMARY) do |record|
367
+ yield record.fields
368
+ end
383
369
 
384
- # A helper to iterate the method provided and return the first record
385
- # where the record's field matches the provided value.
386
- def object_by_field(method, field, value)
387
- send(method).select { |o| o[field] == value }.first
388
- end
370
+ nil
371
+ end
389
372
 
390
- # A helper to iterate the method provided and return the first record
391
- # where the record's fields f1 and f2 match the provided values v1 and v2.
392
- def object_by_two_fields(method, f1, v1, f2, v2)
393
- send(method).select { |o| o[f1] == v1 && o[f2] == v2 }.first
394
- end
373
+ # A helper to iterate the method provided and return the first record
374
+ # where the record's field matches the provided value.
375
+ def object_by_field(method, field, value)
376
+ send(method).select { |o| o[field] == value }.first
377
+ end
395
378
 
396
- # Lookup a table by table ID.
397
- def table_by_id(table_id)
398
- object_by_field(:each_table,
399
- "ID", table_id)
400
- end
379
+ # A helper to iterate the method provided and return the first record
380
+ # where the record's fields f1 and f2 match the provided values v1 and v2.
381
+ def object_by_two_fields(method, field1, value1, field2, value2)
382
+ send(method).select { |o| o[field1] == value1 && o[field2] == value2 }.first
383
+ end
401
384
 
402
- # Lookup a table by table name.
403
- def table_by_name(table_name)
404
- object_by_field(:each_table,
405
- "NAME", table_name)
406
- end
385
+ # Lookup a table by table ID.
386
+ def table_by_id(table_id)
387
+ object_by_field(:each_table, "ID", table_id)
388
+ end
407
389
 
408
- # Lookup a table by space ID.
409
- def table_by_space_id(space_id)
410
- object_by_field(:each_table,
411
- "SPACE", space_id)
412
- end
390
+ # Lookup a table by table name.
391
+ def table_by_name(table_name)
392
+ object_by_field(:each_table, "NAME", table_name)
393
+ end
413
394
 
414
- # Lookup a column by table name and column name.
415
- def column_by_name(table_name, column_name)
416
- unless table = table_by_name(table_name)
417
- return nil
395
+ # Lookup a table by space ID.
396
+ def table_by_space_id(space_id)
397
+ object_by_field(:each_table, "SPACE", space_id)
418
398
  end
419
399
 
420
- object_by_two_fields(:each_column,
421
- "TABLE_ID", table["ID"],
422
- "NAME", column_name)
423
- end
400
+ # Lookup a column by table name and column name.
401
+ def column_by_name(table_name, column_name)
402
+ table = table_by_name(table_name)
403
+ return unless table
424
404
 
425
- # Lookup an index by index ID.
426
- def index_by_id(index_id)
427
- object_by_field(:each_index,
428
- "ID", index_id)
429
- end
405
+ object_by_two_fields(:each_column, "TABLE_ID", table["ID"], "NAME", column_name)
406
+ end
430
407
 
431
- # Lookup an index by table name and index name.
432
- def index_by_name(table_name, index_name)
433
- unless table = table_by_name(table_name)
434
- return nil
408
+ # Lookup an index by index ID.
409
+ def index_by_id(index_id)
410
+ object_by_field(:each_index, "ID", index_id)
435
411
  end
436
412
 
437
- object_by_two_fields(:each_index,
438
- "TABLE_ID", table["ID"],
439
- "NAME", index_name)
440
- end
413
+ # Lookup an index by table name and index name.
414
+ def index_by_name(table_name, index_name)
415
+ table = table_by_name(table_name)
416
+ return unless table
441
417
 
442
- # Iterate through indexes by space ID.
443
- def each_index_by_space_id(space_id)
444
- unless block_given?
445
- return enum_for(:each_index_by_space_id, space_id)
418
+ object_by_two_fields(:each_index, "TABLE_ID", table["ID"], "NAME", index_name)
446
419
  end
447
420
 
448
- each_index do |record|
449
- yield record if record["SPACE"] == space_id
450
- end
421
+ # Iterate through indexes by space ID.
422
+ def each_index_by_space_id(space_id)
423
+ return enum_for(:each_index_by_space_id, space_id) unless block_given?
451
424
 
452
- nil
453
- end
425
+ each_index do |record|
426
+ yield record if record["SPACE"] == space_id
427
+ end
454
428
 
455
- # Iterate through all indexes in a table by table ID.
456
- def each_index_by_table_id(table_id)
457
- unless block_given?
458
- return enum_for(:each_index_by_table_id, table_id)
429
+ nil
459
430
  end
460
431
 
461
- each_index do |record|
462
- yield record if record["TABLE_ID"] == table_id
463
- end
432
+ # Iterate through all indexes in a table by table ID.
433
+ def each_index_by_table_id(table_id)
434
+ return enum_for(:each_index_by_table_id, table_id) unless block_given?
464
435
 
465
- nil
466
- end
436
+ each_index do |record|
437
+ yield record if record["TABLE_ID"] == table_id
438
+ end
467
439
 
468
- # Iterate through all indexes in a table by table name.
469
- def each_index_by_table_name(table_name)
470
- unless block_given?
471
- return enum_for(:each_index_by_table_name, table_name)
440
+ nil
472
441
  end
473
442
 
474
- unless table = table_by_name(table_name)
475
- raise "Table #{table_name} not found"
476
- end
443
+ # Iterate through all indexes in a table by table name.
444
+ def each_index_by_table_name(table_name, &block)
445
+ return enum_for(:each_index_by_table_name, table_name) unless block_given?
477
446
 
478
- each_index_by_table_id(table["ID"]) do |record|
479
- yield record
480
- end
447
+ table = table_by_name(table_name)
448
+ raise "Table #{table_name} not found" unless table
481
449
 
482
- nil
483
- end
450
+ each_index_by_table_id(table["ID"], &block)
484
451
 
485
- # Iterate through all fields in an index by index ID.
486
- def each_field_by_index_id(index_id)
487
- unless block_given?
488
- return enum_for(:each_field_by_index_id, index_id)
452
+ nil
489
453
  end
490
454
 
491
- each_field do |record|
492
- yield record if record["INDEX_ID"] == index_id
493
- end
455
+ # Iterate through all fields in an index by index ID.
456
+ def each_field_by_index_id(index_id)
457
+ return enum_for(:each_field_by_index_id, index_id) unless block_given?
494
458
 
495
- nil
496
- end
459
+ each_field do |record|
460
+ yield record if record["INDEX_ID"] == index_id
461
+ end
497
462
 
498
- # Iterate through all fields in an index by index name.
499
- def each_field_by_index_name(table_name, index_name)
500
- unless block_given?
501
- return enum_for(:each_field_by_name, table_name, index_name)
463
+ nil
502
464
  end
503
465
 
504
- unless index = index_by_name(table_name, index_name)
505
- raise "Index #{index_name} for table #{table_name} not found"
506
- end
466
+ # Iterate through all fields in an index by index name.
467
+ def each_field_by_index_name(table_name, index_name, &block)
468
+ return enum_for(:each_field_by_index_name, table_name, index_name) unless block_given?
507
469
 
508
- each_field_by_index_id(index["ID"]) do |record|
509
- yield record
510
- end
470
+ index = index_by_name(table_name, index_name)
471
+ raise "Index #{index_name} for table #{table_name} not found" unless index
511
472
 
512
- nil
513
- end
473
+ each_field_by_index_id(index["ID"], &block)
514
474
 
515
- # Iterate through all columns in a table by table ID.
516
- def each_column_by_table_id(table_id)
517
- unless block_given?
518
- return enum_for(:each_column_by_table_id, table_id)
475
+ nil
519
476
  end
520
477
 
521
- each_column do |record|
522
- yield record if record["TABLE_ID"] == table_id
523
- end
478
+ # Iterate through all columns in a table by table ID.
479
+ def each_column_by_table_id(table_id)
480
+ return enum_for(:each_column_by_table_id, table_id) unless block_given?
524
481
 
525
- nil
526
- end
482
+ each_column do |record|
483
+ yield record if record["TABLE_ID"] == table_id
484
+ end
527
485
 
528
- # Iterate through all columns in a table by table name.
529
- def each_column_by_table_name(table_name)
530
- unless block_given?
531
- return enum_for(:each_column_by_table_name, table_name)
486
+ nil
532
487
  end
533
488
 
534
- unless table = table_by_name(table_name)
535
- raise "Table #{table_name} not found"
536
- end
489
+ # Iterate through all columns in a table by table name.
490
+ def each_column_by_table_name(table_name, &block)
491
+ return enum_for(:each_column_by_table_name, table_name) unless block_given?
492
+ raise "Table #{table_name} not found" unless (table = table_by_name(table_name))
537
493
 
538
- each_column_by_table_id(table["ID"]) do |record|
539
- yield record
494
+ each_column_by_table_id(table["ID"], &block)
495
+
496
+ nil
540
497
  end
541
498
 
542
- nil
543
- end
499
+ # Iterate through all columns in an index by table name and index name.
500
+ def each_column_in_index_by_name(table_name, index_name)
501
+ return enum_for(:each_column_in_index_by_name, table_name, index_name) unless block_given?
544
502
 
545
- # Iterate through all columns in an index by table name and index name.
546
- def each_column_in_index_by_name(table_name, index_name)
547
- unless block_given?
548
- return enum_for(:each_column_in_index_by_name, table_name, index_name)
549
- end
503
+ each_field_by_index_name(table_name, index_name) do |record|
504
+ yield column_by_name(table_name, record["COL_NAME"])
505
+ end
550
506
 
551
- each_field_by_index_name(table_name, index_name) do |record|
552
- yield column_by_name(table_name, record["COL_NAME"])
507
+ nil
553
508
  end
554
509
 
555
- nil
556
- end
510
+ # Iterate through all columns not in an index by table name and index name.
511
+ # This is useful when building index descriptions for secondary indexes.
512
+ def each_column_not_in_index_by_name(table_name, index_name)
513
+ return enum_for(:each_column_not_in_index_by_name, table_name, index_name) unless block_given?
557
514
 
558
- # Iterate through all columns not in an index by table name and index name.
559
- # This is useful when building index descriptions for secondary indexes.
560
- def each_column_not_in_index_by_name(table_name, index_name)
561
- unless block_given?
562
- return enum_for(:each_column_not_in_index_by_name, table_name, index_name)
563
- end
515
+ columns_in_index = {}
516
+ each_column_in_index_by_name(table_name, index_name) do |record|
517
+ columns_in_index[record["NAME"]] = 1
518
+ end
564
519
 
565
- columns_in_index = {}
566
- each_column_in_index_by_name(table_name, index_name) do |record|
567
- columns_in_index[record["NAME"]] = 1
568
- end
520
+ each_column_by_table_name(table_name) do |record|
521
+ yield record unless columns_in_index.include?(record["NAME"])
522
+ end
569
523
 
570
- each_column_by_table_name(table_name) do |record|
571
- yield record unless columns_in_index.include?(record["NAME"])
524
+ nil
572
525
  end
573
526
 
574
- nil
575
- end
527
+ # Return the name of the clustered index (usually 'PRIMARY', but not always)
528
+ # for a given table name.
529
+ def clustered_index_name_by_table_name(table_name)
530
+ table_record = table_by_name(table_name)
531
+ raise "Table #{table_name} not found" unless table_record
576
532
 
577
- # Return the name of the clustered index (usually "PRIMARY", but not always)
578
- # for a given table name.
579
- def clustered_index_name_by_table_name(table_name)
580
- unless table_record = table_by_name(table_name)
581
- raise "Table #{table_name} not found"
533
+ index_record = object_by_two_fields(:each_index, "TABLE_ID", table_record["ID"], "TYPE", 3)
534
+ index_record["NAME"] if index_record
582
535
  end
583
536
 
584
- if index_record = object_by_two_fields(:each_index,
585
- "TABLE_ID", table_record["ID"],
586
- "TYPE", 3)
587
- index_record["NAME"]
537
+ # Produce a Innodb::RecordDescriber-compatible column description
538
+ # given a type (:key, :row) and data dictionary SYS_COLUMNS record.
539
+ def _make_column_description(type, record)
540
+ {
541
+ type: type,
542
+ name: record["NAME"],
543
+ description: self.class.mtype_prtype_to_data_type(
544
+ record["MTYPE"],
545
+ record["PRTYPE"],
546
+ record["LEN"],
547
+ record["PREC"]
548
+ ),
549
+ }
588
550
  end
589
- end
590
551
 
591
- # Produce a Innodb::RecordDescriber-compatible column description
592
- # given a type (:key, :row) and data dictionary SYS_COLUMNS record.
593
- def _make_column_description(type, record)
594
- {
595
- :type => type,
596
- :name => record["NAME"],
597
- :description => self.class.mtype_prtype_to_data_type(
598
- record["MTYPE"],
599
- record["PRTYPE"],
600
- record["LEN"],
601
- record["PREC"]
602
- )
603
- }
604
- end
552
+ # Iterate through Innodb::RecordDescriber-compatible column descriptions
553
+ # for a given index by table name and index name.
554
+ def each_column_description_by_index_name(table_name, index_name)
555
+ return enum_for(:each_column_description_by_index_name, table_name, index_name) unless block_given?
605
556
 
606
- # Iterate through Innodb::RecordDescriber-compatible column descriptions
607
- # for a given index by table name and index name.
608
- def each_column_description_by_index_name(table_name, index_name)
609
- unless block_given?
610
- return enum_for(:each_column_description_by_index_name, table_name, index_name)
611
- end
557
+ unless (index = index_by_name(table_name, index_name))
558
+ raise "Index #{index_name} for table #{table_name} not found"
559
+ end
612
560
 
613
- unless index = index_by_name(table_name, index_name)
614
- raise "Index #{index_name} for table #{table_name} not found"
615
- end
561
+ columns_in_index = {}
562
+ each_column_in_index_by_name(table_name, index_name) do |record|
563
+ columns_in_index[record["NAME"]] = 1
564
+ yield _make_column_description(:key, record)
565
+ end
616
566
 
617
- columns_in_index = {}
618
- each_column_in_index_by_name(table_name, index_name) do |record|
619
- columns_in_index[record["NAME"]] = 1
620
- yield _make_column_description(:key, record)
621
- end
567
+ if (index["TYPE"] & INDEX_TYPE_FLAG[:CLUSTERED]).zero?
568
+ clustered_index_name = clustered_index_name_by_table_name(table_name)
622
569
 
623
- if index["TYPE"] & INDEX_TYPE_FLAG[:CLUSTERED] != 0
624
- each_column_by_table_name(table_name) do |record|
625
- unless columns_in_index.include?(record["NAME"])
570
+ each_column_in_index_by_name(table_name, clustered_index_name) do |record|
626
571
  yield _make_column_description(:row, record)
627
572
  end
573
+ else
574
+ each_column_by_table_name(table_name) do |record|
575
+ yield _make_column_description(:row, record) unless columns_in_index.include?(record["NAME"])
576
+ end
628
577
  end
629
- else
630
- clustered_index_name = clustered_index_name_by_table_name(table_name)
631
578
 
632
- each_column_in_index_by_name(table_name, clustered_index_name) do |record|
633
- yield _make_column_description(:row, record)
634
- end
579
+ nil
635
580
  end
636
581
 
637
- nil
638
- end
639
-
640
- # Return an Innodb::RecordDescriber object describing records for a given
641
- # index by table name and index name.
642
- def record_describer_by_index_name(table_name, index_name)
643
- if data_dictionary_index?(table_name, index_name)
644
- return data_dictionary_index_describer(table_name, index_name)
645
- end
582
+ # Return an Innodb::RecordDescriber object describing records for a given
583
+ # index by table name and index name.
584
+ def record_describer_by_index_name(table_name, index_name)
585
+ return data_dictionary_index_describer(table_name, index_name) if data_dictionary_index?(table_name, index_name)
646
586
 
647
- unless index = index_by_name(table_name, index_name)
648
- raise "Index #{index_name} for table #{table_name} not found"
649
- end
587
+ unless (index = index_by_name(table_name, index_name))
588
+ raise "Index #{index_name} for table #{table_name} not found"
589
+ end
650
590
 
651
- describer = Innodb::RecordDescriber.new
591
+ describer = Innodb::RecordDescriber.new
652
592
 
653
- if index["TYPE"] & INDEX_TYPE_FLAG[:CLUSTERED] != 0
654
- describer.type :clustered
655
- else
656
- describer.type :secondary
657
- end
593
+ if (index["TYPE"] & INDEX_TYPE_FLAG[:CLUSTERED]).zero?
594
+ describer.type :secondary
595
+ else
596
+ describer.type :clustered
597
+ end
658
598
 
659
- each_column_description_by_index_name(table_name, index_name) do |column|
660
- case column[:type]
661
- when :key
662
- describer.key column[:name], *column[:description]
663
- when :row
664
- describer.row column[:name], *column[:description]
599
+ each_column_description_by_index_name(table_name, index_name) do |column|
600
+ case column[:type]
601
+ when :key
602
+ describer.key column[:name], *column[:description]
603
+ when :row
604
+ describer.row column[:name], *column[:description]
605
+ end
665
606
  end
607
+
608
+ describer
666
609
  end
667
610
 
668
- describer
669
- end
611
+ # Return an Innodb::RecordDescriber object describing the records
612
+ # in a given index by index ID.
613
+ def record_describer_by_index_id(index_id)
614
+ if (dd_index = data_dictionary_index_ids[index_id])
615
+ return data_dictionary_index_describer(dd_index[:table], dd_index[:index])
616
+ end
670
617
 
671
- # Return an Innodb::RecordDescriber object describing the records
672
- # in a given index by index ID.
673
- def record_describer_by_index_id(index_id)
674
- if dd_index = data_dictionary_index_ids[index_id]
675
- return data_dictionary_index_describer(dd_index[:table], dd_index[:index])
676
- end
618
+ unless (index = index_by_id(index_id))
619
+ raise "Index #{index_id} not found"
620
+ end
677
621
 
678
- unless index = index_by_id(index_id)
679
- raise "Index #{index_id} not found"
680
- end
622
+ unless (table = table_by_id(index["TABLE_ID"]))
623
+ raise "Table #{INDEX['TABLE_ID']} not found"
624
+ end
681
625
 
682
- unless table = table_by_id(index["TABLE_ID"])
683
- raise "Table #{INDEX["TABLE_ID"]} not found"
626
+ record_describer_by_index_name(table["NAME"], index["NAME"])
684
627
  end
685
-
686
- record_describer_by_index_name(table["NAME"], index["NAME"])
687
628
  end
688
-
689
629
  end