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.
proxysql/test/tap/mytap-routines.sql

503 lines
17 KiB

-- ROUTINES
-- ========
USE tap;
DELIMITER //
-- work around for STRICT MODE in 5.7
SELECT tap.mysql_version() INTO @version //
SET @mode = (SELECT @@SESSION.sql_mode) //
SET @@SESSION.sql_mode = '' //
/****************************************************************************/
-- internal function to check
DROP FUNCTION IF EXISTS _has_routine //
CREATE FUNCTION _has_routine(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9))
RETURNS BOOLEAN
DETERMINISTIC
COMMENT 'Internal boolean test for the existence of a named routine within the given schema.'
BEGIN
DECLARE ret BOOLEAN;
SELECT 1 INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = rtype;
RETURN COALESCE(ret,0);
END //
-- has_routine(schema, routine, type, description)
DROP FUNCTION IF EXISTS has_routine //
CREATE FUNCTION has_routine(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Test for the existence of a named routine within a given schema.'
BEGIN
IF description = '' THEN
SET description = CONCAT(rtype ,' ',
quote_ident(sname), '.', quote_ident(rname), ' should exist');
END IF;
RETURN ok(_has_routine(sname, rname, rtype), description);
END //
-- hasnt_routine(schema, name, type, description)
DROP FUNCTION IF EXISTS hasnt_routine //
CREATE FUNCTION hasnt_routine(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named routine does not exist within the given schema.'
BEGIN
IF description = '' THEN
SET description = CONCAT(rtype ,' ',
quote_ident(sname), '.', quote_ident(rname), ' should not exist');
END IF;
RETURN ok(NOT _has_routine(sname, rname, rtype), description);
END //
DROP FUNCTION IF EXISTS has_function //
CREATE FUNCTION has_function(sname VARCHAR(64), rname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named function exists within the given schema.'
BEGIN
RETURN has_routine(sname, rname, 'Function', description);
END //
DROP FUNCTION IF EXISTS has_procedure //
CREATE FUNCTION has_procedure(sname VARCHAR(64), rname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named procedure exists within the given schema.'
BEGIN
RETURN has_routine(sname, rname, 'Procedure', description);
END //
DROP FUNCTION IF EXISTS hasnt_function //
CREATE FUNCTION hasnt_function(sname VARCHAR(64), rname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named function does not exist within the given schema.'
BEGIN
RETURN hasnt_routine(sname, rname, 'Function', description);
END //
DROP FUNCTION IF EXISTS hasnt_procedure //
CREATE FUNCTION hasnt_procedure(sname VARCHAR(64), rname VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named procedure does not exist within the given schema.'
BEGIN
RETURN hasnt_routine(sname, rname, 'Procedure', description);
END //
/****************************************************************************/
-- FUNCTION DATA_TYPE i.e. return type
-- NB Procedures have no data_type so only deal with Functions
-- _function_data_type(schema, function, returns, description)
DROP FUNCTION IF EXISTS _function_data_type //
CREATE FUNCTION _function_data_type(sname VARCHAR(64), rname VARCHAR(64))
RETURNS VARCHAR(64)
DETERMINISTIC
COMMENT 'Internal function to return the data type returned by a function.'
BEGIN
DECLARE ret VARCHAR(64);
SELECT `data_type` INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = 'FUNCTION';
RETURN COALESCE(ret, NULL);
END //
-- function_data_type_is(schema, function, returns, description)
DROP FUNCTION IF EXISTS function_data_type_is//
CREATE FUNCTION function_data_type_is(sname VARCHAR(64), rname VARCHAR(64), dtype VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a named function returns the given data type.'
BEGIN
IF description = '' THEN
SET description = concat('Function ', quote_ident(sname), '.', quote_ident(rname),
' should return ', quote_ident(_datatype(dtype)));
END IF;
IF NOT _has_routine(sname, rname, 'FUNCTION') THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Function ', quote_ident(sname),'.', quote_ident(rname),
' does not exist')));
END IF;
RETURN eq(_function_data_type(sname, rname), _datatype(dtype), description);
END //
/****************************************************************************/
-- IS_DETERMINISTIC YES/NO
-- _routine_is_deterministic(schema, name, type, description)
DROP FUNCTION IF EXISTS _routine_is_deterministic //
CREATE FUNCTION _routine_is_deterministic(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9))
RETURNS VARCHAR(3)
DETERMINISTIC
COMMENT 'Internal function to return whether a routine is deterministic.'
BEGIN
DECLARE ret VARCHAR(3);
SELECT `is_deterministic` INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = rtype;
RETURN COALESCE(ret, NULL);
END //
-- routine_is_deterministic(schema, name, type, val, description)
DROP FUNCTION IF EXISTS routine_is_deterministic //
CREATE FUNCTION routine_is_deterministic(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), val VARCHAR(3), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a routine is deterministic.'
BEGIN
IF description = '' THEN
SET description = CONCAT(rtype, ' ', quote_ident(sname), '.', quote_ident(rname),
' should have IS_DETERMINISTIC ', quote_ident(val));
END IF;
IF val NOT IN('YES','NO') THEN
RETURN CONCAT(ok(FALSE, description),'\n',
diag('Is Deterministic must be { YES | NO }'));
END IF;
IF NOT _has_routine(sname, rname, rtype) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT(ucf(rtype), ' ', quote_ident(sname), '.', quote_ident(rname),
' does not exist')));
END IF;
RETURN eq(_routine_is_deterministic(sname, rname, rtype), val, description);
END //
DROP FUNCTION IF EXISTS function_is_deterministic //
CREATE FUNCTION function_is_deterministic(sname VARCHAR(64), rname VARCHAR(64), val VARCHAR(3), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a function is deterministic.'
BEGIN
RETURN routine_is_deterministic(sname, rname, 'Function', val, description);
END //
DROP FUNCTION IF EXISTS procedure_is_deterministic //
CREATE FUNCTION procedure_is_deterministic(sname VARCHAR(64), rname VARCHAR(64), val VARCHAR(3), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a procedure is deterministic.'
BEGIN
RETURN routine_is_deterministic(sname, rname, 'Procedure', val, description);
END //
/****************************************************************************/
-- SECURITY TYPE
-- { INVOKER | DEFINER }
-- _routine_security_type(schema, routine, security_type, description)
DROP FUNCTION IF EXISTS _routine_security_type //
CREATE FUNCTION _routine_security_type(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9))
RETURNS VARCHAR(7)
DETERMINISTIC
COMMENT 'Internal function to return the security type of a routine.'
BEGIN
DECLARE ret VARCHAR(7);
SELECT `security_type` INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = rtype ;
RETURN COALESCE(ret, NULL);
END //
-- routine_security_type_is(schema, name, type, security type , description)
DROP FUNCTION IF EXISTS routine_security_type_is //
CREATE FUNCTION routine_security_type_is(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), stype VARCHAR(7), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the security type of a routine matches the value provided.'
BEGIN
IF description = '' THEN
SET description = CONCAT(rtype, ' ', quote_ident(sname), '.', quote_ident(rname),
' should have Security Type ' , quote_ident(stype));
END IF;
IF stype NOT IN('INVOKER','DEFINER') THEN
RETURN CONCAT(ok(FALSE, description),'\n',
diag('Security Type must be { INVOKER | DEFINER }'));
END IF;
IF NOT _has_routine(sname, rname, rtype) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT(ucf(rtype), ' ', quote_ident(sname), '.', quote_ident(rname), ' does not exist')));
END IF;
RETURN eq(_routine_security_type(sname, rname, rtype), stype, description);
END //
DROP FUNCTION IF EXISTS function_security_type_is //
CREATE FUNCTION function_security_type_is(sname VARCHAR(64), rname VARCHAR(64), stype VARCHAR(7), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the security type of a function matches the value provided.'
BEGIN
RETURN routine_security_type_is(sname, rname, 'Function', stype, description);
END //
DROP FUNCTION IF EXISTS procedure_security_type_is //
CREATE FUNCTION procedure_security_type_is(sname VARCHAR(64), rname VARCHAR(64), stype VARCHAR(7), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the security type of a procedure matches the value provided.'
BEGIN
RETURN routine_security_type_is(sname, rname, 'Procedure', stype, description);
END //
/****************************************************************************/
-- SQL_DATA_ACCESS
-- { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
-- _routine_sql_data_access(schema, routine, type, description)
DROP FUNCTION IF EXISTS _routine_sql_data_access //
CREATE FUNCTION _routine_sql_data_access(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9))
RETURNS VARCHAR(64)
DETERMINISTIC
COMMENT 'Internal function to return the SQL data access value for a named routine within the given schema.'
BEGIN
DECLARE ret VARCHAR(64);
SELECT `sql_data_access` INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = rtype ;
RETURN COALESCE(ret, NULL);
END //
-- function_sql_data_access_is(schema, function, sql data access , description)
DROP FUNCTION IF EXISTS routine_sql_data_access_is //
CREATE FUNCTION routine_sql_data_access_is(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), sda VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the SQL data access value of a routine matches that provided.'
BEGIN
IF description = '' THEN
SET description = CONCAT(rtype ,' ', quote_ident(sname), '.', quote_ident(rname),
' should have SQL Data Access ', quote_ident(sda));
END IF;
IF NOT rtype IN('FUNCTION','PROCEDURE') THEN
RETURN CONCAT(ok(FALSE,description), '\n',
diag('Routine Type must be { FUNCTION | PROCEDURE }'));
END IF;
IF NOT sda IN('CONTAINS SQL','NO SQL','READS SQL DATA','MODIFIES SQL DATA') THEN
RETURN CONCAT(ok(FALSE,description), '\n',
diag('SQL Data Access must be { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }'));
END IF;
IF NOT _has_routine(sname, rname, rtype) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT(ucf(rtype), ' ', quote_ident(sname), '.', quote_ident(rname), ' does not exist')));
END IF;
RETURN eq(_routine_sql_data_access(sname, rname, rtype), sda, description);
END //
DROP FUNCTION IF EXISTS function_sql_data_access_is //
CREATE FUNCTION function_sql_data_access_is(sname VARCHAR(64), rname VARCHAR(64), sda VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the SQL data access value of a function matches that provided.'
BEGIN
RETURN routine_sql_data_access_is(sname, rname, 'Function', sda, description);
END //
DROP FUNCTION IF EXISTS procedure_sql_data_access_is //
CREATE FUNCTION procedure_sql_data_access_is(sname VARCHAR(64), rname VARCHAR(64), sda VARCHAR(64), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Confirm the SQL data access value of a procedure matches that provided.'
BEGIN
RETURN routine_sql_data_access_is(sname, rname, 'Procedure', sda, description);
END //
/*******************************************************************/
-- Check that the proper routines are defined
DROP FUNCTION IF EXISTS routines_are //
CREATE FUNCTION routines_are(sname VARCHAR(64), rtype VARCHAR(9), want TEXT, description TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
SET @want = want;
SET @have = (SELECT GROUP_CONCAT('`',routine_name,'`')
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_type` = rtype);
IF description = '' THEN
SET description = CONCAT('Schema ', quote_ident(sname),
' should have the correct ', LOWER(rtype), 's');
END IF;
IF NOT _has_schema(sname) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT('Schema ', quote_ident(sname), ' 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(CONCAT(rtype, 's'), @extras, @missing, description);
END //
/****************************************************************************/
-- SQL_MODE
-- Checks to ensure appropriate sql mode is available for a function or procedure
-- _routine_has_sql_mode(schema, routine, mode)
DROP FUNCTION IF EXISTS _routine_has_sql_mode //
CREATE FUNCTION _routine_has_sql_mode(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), smode VARCHAR(8192))
RETURNS BOOLEAN
DETERMINISTIC
COMMENT 'Internal function to return the SQL mode which will apply to a routine.'
BEGIN
DECLARE ret BOOLEAN;
SELECT LOCATE(smode, `sql_mode`) INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname
AND `routine_type` = rtype;
RETURN COALESCE(ret, 0);
END //
-- routine_has_sql_mode(schema, name, type, sql_mode , description)
DROP FUNCTION IF EXISTS routine_has_sql_mode //
CREATE FUNCTION routine_has_sql_mode(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(64), smode VARCHAR(8192), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Check that a particular SQL mode will apply to a named routine within the given schema.'
BEGIN
-- this 5.7 list of sql_modes
-- should be fine as a test provide it is superset of previous modes
-- we're only interesed in the name rather than what it does (which does change)
DECLARE valid ENUM('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE',
'NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE',
'POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS',
'NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES',
'STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES',
'ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE',
'NO_ENGINE_SUBSTITUTION','PAD_CHAR_TO_FULL_LENGTH');
DECLARE EXIT HANDLER FOR 1265 -- invalid assignment to enum
RETURN CONCAT(ok(FALSE,description), '\n',
diag(CONCAT('SQL Mode ', quote_ident(smode), ' is invalid')));
IF description = '' THEN
SET description = CONCAT(UPPER(rtype), ' ', quote_ident(sname), '.', quote_ident(rname),
' requires SQL Mode ', quote_ident(smode));
END IF;
SET valid = smode;
IF NOT _has_routine(sname, rname, 'FUNCTION') THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT(UPPER(rtype),' ', quote_ident(sname), '.', quote_ident(rname), ' does not exist')));
END IF;
RETURN ok(_routine_has_sql_mode(sname, rname, rtype, smode), description);
END //
/****************************************************************************/
-- ROUTINE BODY
-- Get the SHA-1 of the routine body to compare for changes
-- allows match against partial value to save typing
-- You can run _routine_sha1 to get the SHA-1, how much of it is used is down to
-- the individual, we can probably ignore the likelihood of collisions.
DROP FUNCTION IF EXISTS _routine_sha1 //
CREATE FUNCTION _routine_sha1(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9))
RETURNS CHAR(40)
DETERMINISTIC
COMMENT 'Internal function to return a SHA1 of the routine body to check for changes over time. This should be easier to maintain than dumping the routine body into the test script.'
BEGIN
DECLARE ret CHAR(40);
SELECT SHA1(`routine_definition`) INTO ret
FROM `information_schema`.`routines`
WHERE `routine_schema` = sname
AND `routine_name` = rname;
RETURN COALESCE(ret, NULL);
END //
DROP FUNCTION IF EXISTS routine_sha1_is //
CREATE FUNCTION routine_sha1_is(sname VARCHAR(64), rname VARCHAR(64), rtype VARCHAR(9), sha1 VARCHAR(40), description TEXT)
RETURNS TEXT
DETERMINISTIC
COMMENT 'Get the SHA1 value of a routine body to compare against a previous value.'
BEGIN
IF description = '' THEN
SET description = CONCAT(ucf(rtype), ' ', quote_ident(sname), '.', quote_ident(rname),
' definition should match expected value');
END IF;
IF NOT _has_routine(sname, rname, rtype) THEN
RETURN CONCAT(ok(FALSE, description), '\n',
diag(CONCAT(ucf(rtype), ' ', quote_ident(sname), '.', quote_ident(rname), ' does not exist')));
END IF;
-- NB length of supplied value not of a SHA-1
RETURN eq(LEFT(_routine_sha1(sname, rname, rtype), LENGTH(sha1)), sha1, description);
END //
DELIMITER ;
SET @@SESSION.sql_mode = @mode;