mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
634 lines
19 KiB
634 lines
19 KiB
-- 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 ;
|