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.
- checksums.yaml +5 -5
- data/README.md +5 -6
- data/bin/innodb_log +13 -18
- data/bin/innodb_space +377 -757
- data/lib/innodb.rb +4 -5
- data/lib/innodb/checksum.rb +26 -24
- data/lib/innodb/data_dictionary.rb +490 -550
- data/lib/innodb/data_type.rb +362 -326
- data/lib/innodb/field.rb +102 -89
- data/lib/innodb/fseg_entry.rb +22 -26
- data/lib/innodb/history.rb +21 -21
- data/lib/innodb/history_list.rb +72 -76
- data/lib/innodb/ibuf_bitmap.rb +36 -36
- data/lib/innodb/ibuf_index.rb +6 -2
- data/lib/innodb/index.rb +245 -276
- data/lib/innodb/inode.rb +154 -155
- data/lib/innodb/list.rb +191 -183
- data/lib/innodb/log.rb +139 -110
- data/lib/innodb/log_block.rb +100 -91
- data/lib/innodb/log_group.rb +53 -64
- data/lib/innodb/log_reader.rb +97 -96
- data/lib/innodb/log_record.rb +328 -279
- data/lib/innodb/lsn.rb +86 -81
- data/lib/innodb/page.rb +417 -414
- data/lib/innodb/page/blob.rb +82 -83
- data/lib/innodb/page/fsp_hdr_xdes.rb +174 -165
- data/lib/innodb/page/ibuf_bitmap.rb +34 -34
- data/lib/innodb/page/index.rb +964 -943
- data/lib/innodb/page/index_compressed.rb +34 -34
- data/lib/innodb/page/inode.rb +103 -112
- data/lib/innodb/page/sys.rb +13 -15
- data/lib/innodb/page/sys_data_dictionary_header.rb +81 -59
- data/lib/innodb/page/sys_ibuf_header.rb +45 -42
- data/lib/innodb/page/sys_rseg_header.rb +88 -82
- data/lib/innodb/page/trx_sys.rb +204 -182
- data/lib/innodb/page/undo_log.rb +106 -92
- data/lib/innodb/record.rb +121 -160
- data/lib/innodb/record_describer.rb +66 -68
- data/lib/innodb/space.rb +380 -418
- data/lib/innodb/stats.rb +33 -35
- data/lib/innodb/system.rb +149 -171
- data/lib/innodb/undo_log.rb +129 -107
- data/lib/innodb/undo_record.rb +255 -247
- data/lib/innodb/util/buffer_cursor.rb +81 -79
- data/lib/innodb/util/read_bits_at_offset.rb +2 -1
- data/lib/innodb/version.rb +2 -2
- data/lib/innodb/xdes.rb +144 -142
- metadata +80 -11
data/lib/innodb.rb
CHANGED
@@ -1,21 +1,20 @@
|
|
1
|
-
#
|
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
|
-
|
6
|
+
@debug = false
|
7
7
|
|
8
8
|
def self.debug?
|
9
|
-
|
9
|
+
@debug == true
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.debug=(value)
|
13
|
-
|
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"
|
data/lib/innodb/checksum.rb
CHANGED
@@ -1,31 +1,33 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Innodb
|
4
|
+
class Checksum
|
5
|
+
MAX = 0xFFFFFFFF
|
6
|
+
MASK1 = 1_463_735_687
|
7
|
+
MASK2 = 1_653_893_711
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
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
|
-
#
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
:
|
63
|
-
:
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
:
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
:
|
109
|
-
|
110
|
-
:
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
:
|
115
|
-
:
|
116
|
-
|
117
|
-
:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
203
|
-
data_type <<
|
204
|
-
|
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
|
-
|
209
|
-
data_type << :NOT_NULL
|
222
|
+
data_type
|
210
223
|
end
|
211
224
|
|
212
|
-
|
213
|
-
data_type << :UNSIGNED
|
214
|
-
end
|
225
|
+
attr_reader :system_space
|
215
226
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
attr_reader :system_space
|
227
|
+
def initialize(system_space)
|
228
|
+
@system_space = system_space
|
229
|
+
end
|
220
230
|
|
221
|
-
|
222
|
-
|
223
|
-
|
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
|
-
|
226
|
-
|
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
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
:
|
243
|
-
:
|
249
|
+
table: table,
|
250
|
+
index: index,
|
244
251
|
}
|
245
252
|
end
|
246
253
|
end
|
247
|
-
end
|
248
254
|
|
249
|
-
|
250
|
-
|
255
|
+
@data_dictionary_index_ids
|
256
|
+
end
|
251
257
|
|
252
|
-
|
253
|
-
|
254
|
-
|
258
|
+
def data_dictionary_table?(table_name)
|
259
|
+
DATA_DICTIONARY_RECORD_DESCRIBERS.include?(table_name.to_sym)
|
260
|
+
end
|
255
261
|
|
256
|
-
|
257
|
-
|
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
|
-
|
262
|
-
|
265
|
+
DATA_DICTIONARY_RECORD_DESCRIBERS[table_name.to_sym].include?(index_name.to_sym)
|
266
|
+
end
|
263
267
|
|
264
|
-
|
265
|
-
|
268
|
+
def data_dictionary_index_describer(table_name, index_name)
|
269
|
+
return unless data_dictionary_index?(table_name, index_name)
|
266
270
|
|
267
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
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
|
-
|
280
|
-
|
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
|
-
|
283
|
-
|
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
|
-
|
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
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
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
|
-
|
299
|
-
|
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
|
-
|
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
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
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
|
-
|
316
|
-
|
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
|
-
|
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
|
-
|
326
|
-
|
327
|
-
|
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
|
-
|
330
|
-
end
|
324
|
+
data_dictionary_index(table, index).each_record(&block)
|
331
325
|
|
332
|
-
|
333
|
-
def each_table
|
334
|
-
unless block_given?
|
335
|
-
return enum_for(:each_table)
|
326
|
+
nil
|
336
327
|
end
|
337
328
|
|
338
|
-
|
339
|
-
|
340
|
-
|
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
|
-
|
343
|
-
|
333
|
+
each_record_from_data_dictionary_index(:SYS_TABLES, :PRIMARY) do |record|
|
334
|
+
yield record.fields
|
335
|
+
end
|
344
336
|
|
345
|
-
|
346
|
-
def each_column
|
347
|
-
unless block_given?
|
348
|
-
return enum_for(:each_column)
|
337
|
+
nil
|
349
338
|
end
|
350
339
|
|
351
|
-
|
352
|
-
|
353
|
-
|
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
|
-
|
356
|
-
|
344
|
+
each_record_from_data_dictionary_index(:SYS_COLUMNS, :PRIMARY) do |record|
|
345
|
+
yield record.fields
|
346
|
+
end
|
357
347
|
|
358
|
-
|
359
|
-
def each_index
|
360
|
-
unless block_given?
|
361
|
-
return enum_for(:each_index)
|
348
|
+
nil
|
362
349
|
end
|
363
350
|
|
364
|
-
|
365
|
-
|
366
|
-
|
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
|
-
|
369
|
-
|
355
|
+
each_record_from_data_dictionary_index(:SYS_INDEXES, :PRIMARY) do |record|
|
356
|
+
yield record.fields
|
357
|
+
end
|
370
358
|
|
371
|
-
|
372
|
-
def each_field
|
373
|
-
unless block_given?
|
374
|
-
return enum_for(:each_field)
|
359
|
+
nil
|
375
360
|
end
|
376
361
|
|
377
|
-
|
378
|
-
|
379
|
-
|
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
|
-
|
382
|
-
|
366
|
+
each_record_from_data_dictionary_index(:SYS_FIELDS, :PRIMARY) do |record|
|
367
|
+
yield record.fields
|
368
|
+
end
|
383
369
|
|
384
|
-
|
385
|
-
|
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
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
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
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
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
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
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
|
-
|
415
|
-
|
416
|
-
|
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
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
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
|
-
|
426
|
-
|
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
|
-
|
432
|
-
|
433
|
-
|
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
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
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
|
-
|
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
|
-
|
449
|
-
|
450
|
-
|
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
|
-
|
453
|
-
|
425
|
+
each_index do |record|
|
426
|
+
yield record if record["SPACE"] == space_id
|
427
|
+
end
|
454
428
|
|
455
|
-
|
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
|
-
|
462
|
-
|
463
|
-
|
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
|
-
|
466
|
-
|
436
|
+
each_index do |record|
|
437
|
+
yield record if record["TABLE_ID"] == table_id
|
438
|
+
end
|
467
439
|
|
468
|
-
|
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
|
-
|
475
|
-
|
476
|
-
|
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
|
-
|
479
|
-
|
480
|
-
end
|
447
|
+
table = table_by_name(table_name)
|
448
|
+
raise "Table #{table_name} not found" unless table
|
481
449
|
|
482
|
-
|
483
|
-
end
|
450
|
+
each_index_by_table_id(table["ID"], &block)
|
484
451
|
|
485
|
-
|
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
|
-
|
492
|
-
|
493
|
-
|
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
|
-
|
496
|
-
|
459
|
+
each_field do |record|
|
460
|
+
yield record if record["INDEX_ID"] == index_id
|
461
|
+
end
|
497
462
|
|
498
|
-
|
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
|
-
|
505
|
-
|
506
|
-
|
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
|
-
|
509
|
-
|
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
|
-
|
513
|
-
end
|
473
|
+
each_field_by_index_id(index["ID"], &block)
|
514
474
|
|
515
|
-
|
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
|
-
|
522
|
-
|
523
|
-
|
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
|
-
|
526
|
-
|
482
|
+
each_column do |record|
|
483
|
+
yield record if record["TABLE_ID"] == table_id
|
484
|
+
end
|
527
485
|
|
528
|
-
|
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
|
-
|
535
|
-
|
536
|
-
|
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
|
-
|
539
|
-
|
494
|
+
each_column_by_table_id(table["ID"], &block)
|
495
|
+
|
496
|
+
nil
|
540
497
|
end
|
541
498
|
|
542
|
-
|
543
|
-
|
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
|
-
|
546
|
-
|
547
|
-
|
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
|
-
|
552
|
-
yield column_by_name(table_name, record["COL_NAME"])
|
507
|
+
nil
|
553
508
|
end
|
554
509
|
|
555
|
-
|
556
|
-
|
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
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
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
|
-
|
566
|
-
|
567
|
-
|
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
|
-
|
571
|
-
yield record unless columns_in_index.include?(record["NAME"])
|
524
|
+
nil
|
572
525
|
end
|
573
526
|
|
574
|
-
|
575
|
-
|
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
|
-
|
578
|
-
|
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
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
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
|
-
|
592
|
-
|
593
|
-
|
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
|
-
|
607
|
-
|
608
|
-
|
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
|
-
|
614
|
-
|
615
|
-
|
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
|
-
|
618
|
-
|
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
|
-
|
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
|
-
|
633
|
-
yield _make_column_description(:row, record)
|
634
|
-
end
|
579
|
+
nil
|
635
580
|
end
|
636
581
|
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
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
|
-
|
648
|
-
|
649
|
-
|
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
|
-
|
591
|
+
describer = Innodb::RecordDescriber.new
|
652
592
|
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
593
|
+
if (index["TYPE"] & INDEX_TYPE_FLAG[:CLUSTERED]).zero?
|
594
|
+
describer.type :secondary
|
595
|
+
else
|
596
|
+
describer.type :clustered
|
597
|
+
end
|
658
598
|
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
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
|
-
|
669
|
-
|
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
|
-
|
672
|
-
|
673
|
-
|
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
|
-
|
679
|
-
|
680
|
-
|
622
|
+
unless (table = table_by_id(index["TABLE_ID"]))
|
623
|
+
raise "Table #{INDEX['TABLE_ID']} not found"
|
624
|
+
end
|
681
625
|
|
682
|
-
|
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
|