-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathmytap-constraint.sql
633 lines (521 loc) · 19.2 KB
/
mytap-constraint.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
-- CONSTRAINTS
-- ===========
-- PRIMARY KEY, FOREIGN KEY and UNIQUE constraints
USE tap;
DELIMITER //
-- Simple check on existence of named constraint without being concerned for its
-- composition
DROP FUNCTION IF EXISTS _has_constraint //
CREATE FUNCTION _has_constraint(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64))
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret INT;
SELECT COUNT(*) INTO ret
FROM `information_schema`.`table_constraints`
WHERE `constraint_schema` = sname
AND `table_name` = tname
AND `constraint_name` = cname;
RETURN IF(ret > 0 , 1, 0);
END //
-- check for the existence of named constraint
DROP FUNCTION IF EXISTS has_constraint //
CREATE FUNCTION has_constraint(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' 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_constraint(sname, tname, cname), description);
END //
-- test for when constraint has been removed
DROP FUNCTION IF EXISTS hasnt_constraint //
CREATE FUNCTION hasnt_constraint(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Constraint ', quote_ident(tname),'.',quote_ident(cname),
' 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_constraint(sname, tname, cname), description);
END //
/********************************************************************************/
DROP FUNCTION IF EXISTS _has_constraint_type //
CREATE FUNCTION _has_constraint_type(sname VARCHAR(64), tname VARCHAR(64), ctype VARCHAR(64))
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret INT;
SELECT COUNT(*) INTO ret
FROM `information_schema`.`table_constraints`
WHERE `constraint_schema` = sname
AND `table_name` = tname
AND `constraint_type` = ctype;
RETURN IF(ret > 0 , 1, 0);
END //
-- PRIMARY KEY exists
DROP FUNCTION IF EXISTS has_pk //
CREATE FUNCTION has_pk(sname VARCHAR(64), tname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have a Primary Key');
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_constraint(sname, tname, 'PRIMARY'), description);
END //
DROP FUNCTION IF EXISTS hasnt_pk //
CREATE FUNCTION hasnt_pk(sname VARCHAR(64), tname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should not have a Primary Key');
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;
-- PK is ALWAYS called PRIMARY but could have used _has_constraint_type here
RETURN ok(NOT _has_constraint(sname, tname, 'PRIMARY'), description);
END //
-- Loose check on the existence of an FK on the table
DROP FUNCTION IF EXISTS has_fk //
CREATE FUNCTION has_fk(sname VARCHAR(64), tname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have a Foreign Key');
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_constraint_type(sname, tname, 'FOREIGN KEY'), description);
END //
DROP FUNCTION IF EXISTS hasnt_fk //
CREATE FUNCTION hasnt_fk(sname VARCHAR(64), tname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should not have a Foreign Key');
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_constraint_type(sname, tname, 'FOREIGN KEY'), description);
END //
-- Check composition of an index is unique
-- This is an index check rather than a column test since we can test multiple cols
DROP FUNCTION IF EXISTS _col_is_unique //
CREATE FUNCTION _col_is_unique(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
AND `non_unique` = 0
GROUP BY `table_name`,`index_name`
) indices
WHERE `indexdef` = want;
RETURN IF(ret > 0 , 1, 0);
END //
-- Does the column or column list have an index that is unique (ie UNIQUE or PRIMARY),
-- save for later an intelligent way of testing the existence of the cols in want
-- Oh for a postgres array
DROP FUNCTION IF EXISTS col_is_unique //
CREATE FUNCTION col_is_unique(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
-- quote a single identifier if the user forgot
IF NOT LOCATE(',', want) AND NOT LOCATE('`', want) THEN
SET want = CONCAT('`', want, '`');
END IF;
IF description = '' THEN
SET description = CONCAT('Unique 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(_col_is_unique( sname, tname, want), description);
END //
-- Check cols make a PRIMARY KEY
-- This is an index check rather than a column test since we can test multiple cols
-- pgTAP functions index_is_clustered() and index_is_primary() on named index are not
-- required because the PK is always clustered and it's always called 'PRIMARY'
DROP FUNCTION IF EXISTS _col_is_pk //
CREATE FUNCTION _col_is_pk(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 `index_name` = 'PRIMARY'
AND `indexdef` = want;
RETURN IF(ret <> 0 , TRUE, FALSE);
END //
-- Does the column or column list form a PK
DROP FUNCTION IF EXISTS col_is_pk //
CREATE FUNCTION col_is_pk(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF NOT LOCATE(',', want) AND NOT LOCATE('`', want) THEN
SET want = CONCAT('`', want, '`'); -- quote_ident(want);
END IF;
IF description = '' THEN
SET description = CONCAT('Primary Key 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(_col_is_pk( sname, tname, want), description);
END //
-- Check a unique index exists on a table - it will if there's a PK
-- perhaps relocate to mysql-table
DROP FUNCTION IF EXISTS _has_unique //
CREATE FUNCTION _has_unique(sname VARCHAR(64), tname VARCHAR(64))
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret BOOLEAN;
SELECT COUNT(`table_name`) INTO ret
FROM `information_schema`.`statistics`
WHERE `table_schema` = sname
AND `table_name` = tname
AND `non_unique` = 0;
RETURN IF(ret <> 0 , TRUE, FALSE);
END //
-- Does a table have an index that is unique (ie UNIQUE or PRIMARY),
DROP FUNCTION IF EXISTS has_unique //
CREATE FUNCTION has_unique(sname VARCHAR(64), tname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have a Unique Index');
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_unique(sname, tname), description);
END //
/***************************************************************************/
-- Constraint Type
-- FK, PK or UNIQUE
DROP FUNCTION IF EXISTS _constraint_type //
CREATE FUNCTION _constraint_type(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64))
RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE ret VARCHAR(64);
SELECT `constraint_type` INTO ret
FROM `information_schema`.`table_constraints`
WHERE `constraint_schema` = sname
AND `table_name` = tname
AND `constraint_name` = cname;
RETURN ret;
END //
DROP FUNCTION IF EXISTS constraint_type_is //
CREATE FUNCTION constraint_type_is(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64), ctype VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' should have Constraint Type ' , qv(ctype));
END IF;
IF NOT _has_constraint(sname, tname, cname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' does not exist')));
END IF;
RETURN eq(_constraint_type(sname, tname, cname), ctype, description);
END //
/***************************************************************************/
-- FK Properties
-- on delete, on update. on match is ALWAYS None
DROP FUNCTION IF EXISTS _fk_on_delete //
CREATE FUNCTION _fk_on_delete(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64))
RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE ret VARCHAR(64);
SELECT `delete_rule` INTO ret
FROM `information_schema`.`referential_constraints`
WHERE `constraint_schema` = sname
AND `table_name` = tname
AND `constraint_name` = cname;
RETURN ret;
END //
-- check for rule ON DELETE
DROP FUNCTION IF EXISTS fk_on_delete //
CREATE FUNCTION fk_on_delete(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64), rule VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' should have rule ON DELETE ', qv(rule));
END IF;
IF NOT _has_constraint(sname, tname, cname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' does not exist')));
END IF;
RETURN eq(_fk_on_delete(sname, tname, cname), rule, description);
END //
DROP FUNCTION IF EXISTS _fk_on_update //
CREATE FUNCTION _fk_on_update(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64))
RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE ret VARCHAR(64);
SELECT `update_rule` INTO ret
FROM `information_schema`.`referential_constraints`
WHERE `constraint_schema` = sname
AND `table_name` = tname
AND `constraint_name` = cname;
RETURN ret;
END //
-- check for rule ON UPDATE
DROP FUNCTION IF EXISTS fk_on_update //
CREATE FUNCTION fk_on_update(sname VARCHAR(64), tname VARCHAR(64), cname VARCHAR(64), rule VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF description = '' THEN
SET description = CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' should have rule ON UPDATE ' , qv(rule));
END IF;
IF NOT _has_constraint(sname, tname, cname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Constraint ', quote_ident(tname), '.', quote_ident(cname),
' does not exist')));
END IF;
RETURN eq(_fk_on_update(sname, tname, cname), rule, description);
END //
/***************************************************************************/
DROP FUNCTION IF EXISTS _fk_ok //
CREATE FUNCTION _fk_ok(csch VARCHAR(64), ctab VARCHAR(64), ccol TEXT,
usch VARCHAR(64), utab VARCHAR(64), ucol TEXT)
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
DECLARE ret BOOLEAN;
SELECT COUNT(*) INTO ret
FROM
(
SELECT kc.`constraint_schema` AS `csch`,
kc.`table_name` AS `ctab`,
GROUP_CONCAT(CONCAT('`',kc.`column_name`,'`') ORDER BY kc.`ordinal_position`) AS `cols1`,
kc.`referenced_table_schema` AS `usch`,
kc.`referenced_table_name` AS `utab`,
GROUP_CONCAT(CONCAT('`',kc.`referenced_column_name`,'`') ORDER BY `position_in_unique_constraint`) AS `cols2`
FROM `information_schema`.`key_column_usage` kc
WHERE kc.`constraint_schema` = csch AND kc.`referenced_table_schema` = usch
AND kc.`table_name` = ctab AND kc.`referenced_table_name` = utab
GROUP BY 1,2,4,5
HAVING GROUP_CONCAT(CONCAT('`',kc.`column_name`,'`') ORDER BY kc.`ordinal_position`) = ccol
AND GROUP_CONCAT(CONCAT('`',kc.`referenced_column_name`,'`') ORDER BY `position_in_unique_constraint`) = ucol
) fkey;
RETURN COALESCE(ret,0);
END //
-- check that a foreign key points to the correct table and indexed columns key
-- cname and uname will likly be single columns but they may not be, the index
-- references will therefore have to be resolved before they can be compared
-- ccols an ucols must be quoted for comparison!
DROP FUNCTION IF EXISTS fk_ok //
CREATE FUNCTION fk_ok(csch VARCHAR(64), ctab VARCHAR(64), ccol TEXT,
usch VARCHAR(64), utab VARCHAR(64), ucol TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
IF NOT LOCATE(',', ccol) AND NOT LOCATE('`', ccol) THEN
SET ccol = CONCAT('`', ccol, '`');
END IF;
IF NOT LOCATE(',', ucol) AND NOT LOCATE('`', ucol) THEN
SET ucol = CONCAT('`', ucol, '`');
END IF;
IF description = '' THEN
SET description = CONCAT('Constraint Foreign Key ', quote_ident(ctab), '(', ccol,
') should reference ' , quote_ident(utab), '(', ucol, ')');
END IF;
IF NOT _has_table(csch, ctab) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(csch), '.', quote_ident(ctab),
' does not exist')));
END IF;
IF NOT _has_table(usch, utab) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Table ', quote_ident(usch), '.', quote_ident(utab),
' does not exist')));
END IF;
RETURN ok(_fk_ok(csch, ctab, ccol, usch, utab, ucol), description);
END //
/*******************************************************************/
-- Check that the proper constraints are defined
/*
DROP FUNCTION IF EXISTS _missing_constraints //
CREATE FUNCTION _missing_constraints(sname VARCHAR(64), tname VARCHAR(64))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE ret TEXT;
SELECT GROUP_CONCAT(quote_ident(`ident`)) INTO ret
FROM
(
SELECT `ident`
FROM `idents1`
WHERE `ident` NOT IN
(
SELECT `constraint_name`
FROM `information_schema`.`table_constraints`
WHERE `table_schema` = sname
AND `table_name` = tname
)
) msng;
RETURN COALESCE(ret, '');
END //
DROP FUNCTION IF EXISTS _extra_constraints //
CREATE FUNCTION _extra_constraints(sname VARCHAR(64), tname VARCHAR(64))
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE ret TEXT;
SELECT GROUP_CONCAT(quote_ident(`ident`)) into ret FROM
(
SELECT DISTINCT `constraint_name` AS `ident`
FROM `information_schema`.`table_constraints`
WHERE `table_schema` = sname
AND `table_name` = tname
AND `constraint_name` NOT IN
(
SELECT `ident`
FROM `idents2`
)
) xtra;
RETURN COALESCE(ret, '');
END //
DROP FUNCTION IF EXISTS constraints_are //
CREATE FUNCTION constraints_are(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE sep CHAR(1) DEFAULT ',';
DECLARE seplength INTEGER DEFAULT CHAR_LENGTH(sep);
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have the correct Constraints');
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;
SET want = _fixCSL(want);
IF want IS NULL THEN
RETURN CONCAT(ok(FALSE,description),'\n',
diag(CONCAT('Invalid character in comma separated list of expected schemas\n',
'Identifier must not contain NUL Byte or extended characters (> U+10000)')));
END IF;
DROP TEMPORARY TABLE IF EXISTS idents1;
CREATE TEMPORARY TABLE tap.idents1 (ident VARCHAR(64) PRIMARY KEY)
ENGINE MEMORY CHARSET utf8 COLLATE utf8_general_ci;
DROP TEMPORARY TABLE IF EXISTS idents2;
CREATE TEMPORARY TABLE tap.idents2 (ident VARCHAR(64) PRIMARY KEY)
ENGINE MEMORY CHARSET utf8 COLLATE utf8_general_ci;
WHILE want != '' > 0 DO
SET @val = TRIM(SUBSTRING_INDEX(want, sep, 1));
SET @val = uqi(@val);
IF @val <> '' THEN
INSERT IGNORE INTO idents1 VALUE(@val);
INSERT IGNORE INTO idents2 VALUE(@val);
END IF;
SET want = SUBSTRING(want, CHAR_LENGTH(@val) + seplength + 1);
END WHILE;
SET @missing = _missing_constraints(sname, tname);
SET @extras = _extra_constraints(sname, tname);
RETURN _are('constraints', @extras, @missing, description);
END //
*/
DROP FUNCTION IF EXISTS constraints_are //
CREATE FUNCTION constraints_are(sname VARCHAR(64), tname VARCHAR(64), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
SET @want = want;
SET @have = (SELECT GROUP_CONCAT('`', `constraint_name`,'`')
FROM `information_schema`.`table_constraints`
WHERE `table_schema` = sname
AND `table_name` = tname);
IF description = '' THEN
SET description = CONCAT('Table ', quote_ident(sname), '.', quote_ident(tname),
' should have the correct Constraints');
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('constraints', @extras, @missing, description);
END //
DELIMITER ;