-
Notifications
You must be signed in to change notification settings - Fork 31
/
mytap-index.sql
255 lines (205 loc) · 7.72 KB
/
mytap-index.sql
1
2
3
4
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
-- INDEX check functions
-- =====================
USE tap;
DELIMITER //
/**************************************************************************/
-- Check constituent parts of an index covers partial index
DROP FUNCTION IF EXISTS _index_def //
CREATE FUNCTION _index_def(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE ret TEXT;
SELECT GROUP_CONCAT(CONCAT('`', `column_name`, '`'),
IF(`sub_part` IS NULL, '', CONCAT('(', `sub_part`, ')'))) AS 'column_name' INTO ret
FROM `information_schema`.`statistics`
WHERE `table_schema` = sname
AND `table_name` = tname
AND `index_name` = iname
ORDER BY `seq_in_index`;
RETURN ret;
END //
-- The named index should comprise ...
-- pgTAP has this test as part of has_index, which is possible with postgres function overloading
-- a separate function is required for mysql.
-- Includes a test for partial index in format `name`(n)
-- Quote everything in the 'want' CSL string with backticks and no spaces
DROP FUNCTION IF EXISTS index_is //
CREATE FUNCTION index_is(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' should exist on ' , want);
END IF;
IF NOT _has_index(sname, tname, iname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' does not exist' )));
END IF;
RETURN eq(_index_def(sname, tname, iname), want, description);
END //
-- Check constituent parts of an index
-- We expect a comma separated list of quoted identifiers
DROP FUNCTION IF EXISTS _is_indexed //
CREATE FUNCTION _is_indexed(sname VARCHAR(64), tname VARCHAR(64), want TEXT)
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret BOOLEAN;
SELECT COUNT(`indexdef`) INTO ret
FROM
(
SELECT `table_name`, `index_name`,
GROUP_CONCAT(CONCAT('`', `column_name`, '`') ORDER BY `seq_in_index`) AS `indexdef`
FROM `information_schema`.`statistics`
WHERE `table_schema` = sname
AND `table_name` = tname
GROUP BY `table_name`,`index_name`
) indices
WHERE `indexdef` = want;
RETURN IF(ret <> 0 , TRUE, FALSE);
END //
-- Simply, is there an index covering the columns supplied (in the order provided),
-- we only care that the names are quoted for the check and there are no spaces in the CSL
DROP FUNCTION IF EXISTS is_indexed //
CREATE FUNCTION is_indexed(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Index for ', quote_ident(sname), '.', quote_ident(tname),
' should exist on ' , want);
END IF;
IF NOT _has_table( sname, tname ) THEN
RETURN CONCAT(ok( FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' does not exist' )));
END IF;
RETURN ok(_is_indexed(sname, tname, want), description);
END //
/*****************************************************************************/
-- Simple check on existence of named index without being concerned for its
-- composition
DROP FUNCTION IF EXISTS _has_index //
CREATE FUNCTION _has_index(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64))
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret BOOLEAN;
SELECT 1 INTO ret
FROM `information_schema`.`statistics`
WHERE `table_schema` = sname
AND `table_name` = tname
AND `index_name` = iname
LIMIT 1; -- for multi column index
RETURN COALESCE(ret, 0);
END //
-- check for the existence of named index
DROP FUNCTION IF EXISTS has_index //
CREATE FUNCTION has_index(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' should exist');
END IF;
IF NOT _has_table(sname, tname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' does not exist')));
END IF;
RETURN ok(_has_index( sname, tname, iname), description);
END //
-- test for when an index has been deleted, would also show in the extras list of indexes_are()
DROP FUNCTION IF EXISTS hasnt_index //
CREATE FUNCTION hasnt_index(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' should not exist');
END IF;
IF NOT _has_table( sname, tname ) THEN
RETURN CONCAT( ok( FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' does not exist')));
END IF;
RETURN ok(NOT _has_index(sname, tname, iname), description);
END //
/*******************************************************************/
-- Checks for index properties
-- BTREE, FULLTEXT, SPATIAL
DROP FUNCTION IF EXISTS _index_type //
CREATE FUNCTION _index_type(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64))
RETURNS VARCHAR(16)
DETERMINISTIC
BEGIN
DECLARE ret VARCHAR(16);
SELECT `index_type` INTO ret
FROM `information_schema`.`statistics`
WHERE `table_schema` = sname
AND `table_name` = tname
AND `index_name` = iname
LIMIT 1; -- for multi-col index
RETURN COALESCE(ret, NULL);
END //
DROP FUNCTION IF EXISTS index_is_type //
CREATE FUNCTION index_is_type(sname VARCHAR(64), tname VARCHAR(64), iname VARCHAR(64), itype VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' should be of Type ', qv(itype));
END IF;
IF NOT _has_table( sname, tname ) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
'does not exist' )));
END IF;
-- REM index names a
IF NOT _has_index( sname, tname, iname ) THEN
RETURN CONCAT(ok(FALSE,description),'\n',
diag(CONCAT('Index ', quote_ident(tname), '.', quote_ident(iname),
' does not exist')));
END IF;
RETURN eq(_index_type( sname, tname, iname), itype, description);
END //
/*******************************************************************/
-- Check that the proper indexes are defined
-- Table constraints are handled elsewhere
DROP FUNCTION IF EXISTS indexes_are //
CREATE FUNCTION indexes_are(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
SET @want = want;
SET @have = (SELECT GROUP_CONCAT('`', s.`index_name`,'`')
FROM `information_schema`.`statistics` s
LEFT JOIN `information_schema`.`table_constraints` c
ON (s.`table_schema` = c.`table_schema`
AND s.`table_name` = c.`table_name`
AND s.`index_name` = c.`constraint_name`)
WHERE s.`table_schema` = sname
AND s.`table_name` = tname
AND c.`constraint_name` IS NULL);
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have the correct indexes');
END IF;
IF NOT _has_table(sname, tname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' does not exist' )));
END IF;
CALL _populate_want(@want);
CALL _populate_have(@have);
SET @missing = (SELECT _missing(@have));
SET @extras = (SELECT _extra(@want));
RETURN _are('indexes', @extras, @missing, description);
END //
DELIMITER ;