I'm a bit hesitant about recommending this method so consider the following as proof of concept rather than a tried and tested approach but it is possible to create a stored routine (procedure or function) within a stored procedure.
The answer by @AC is correct if you want to use the standard CREATE PROCEDURE p_name () ... syntax from inside another procedure. However, procedures and functions are stored in the mysql.proc table, if you have INSERT permissions on that table you can add the routine's parameters, body and associated meta data as values directly into the appropriate columns.
Provided you decompose your routine so that you insert to the correct columns it will work (proof of concept below). However, while errors on some columns will result in helpful messages that say why a particular value is inappropriate, syntax errors in the params_list,body and body_utf8 columns will not be caught by this method and will cause a nasty looking runtime error when you try to use it. e.g.
Error Code: 1457. Failed to load routine test.from_proc. The table
mysql.proc is missing, corrupt, or contains bad data (internal code
-6)
Fortunately, it's the 'bad data' bit that matters here and it's easily rectified either by UPDATE or simply dropping the offending routine. However, the absence of any syntax checking means that you would need to test thoroughly before deploying this method.
Procedure
DELIMITER //
DROP PROCEDURE IF EXISTS create_routine //
CREATE PROCEDURE create_routine (
_db CHAR(64),
_name CHAR(64),
_type CHAR(9),
_sql_data_access CHAR(17),
_is_deterministic CHAR(3),
_security_type CHAR(7),
_param_list BLOB,
_returns LONGBLOB,
_body LONGBLOB,
_comment TEXT)
MODIFIES SQL DATA
BEGIN
SET @sql_mode = (SELECT @@SESSION.sql_mode);
SET @character_set_client = (SELECT @@SESSION.character_set_client);
SET @collation_connection = (SELECT @@SESSION.collation_connection);
SET @db_collation = (SELECT @@SESSION.collation_database);
INSERT INTO `mysql`.`proc` SET
`db` = _db,
`name` = _name,
`type` = _type,
`specific_name` = _name,
`sql_data_access` = _sql_data_access,
`is_deterministic` = _is_deterministic,
`security_type` = _security_type,
`param_list` = _param_list,
`returns` = _returns,
`body` = _body,
`definer` = CURRENT_USER(),
`modified` = NOW(),
`sql_mode` = @sql_mode,
`comment` = _comment,
`character_set_client` = @character_set_client,
`collation_connection` = @collation_connection,
`db_collation` = @db_collation,
`body_utf8` = _body;
END //
DELIMITER ;
Usage
SET @SQL =
"BEGIN
SELECT UPPER(_in);
END";
call create_routine(
'test',
'proc_test',
'PROCEDURE',
'CONTAINS_SQL',
'YES',
'INVOKER',
'IN _in CHAR(3)',
'',
@SQL,
'Procedure generated by procedure'
);
call create_routine(
'test',
'func_test',
'FUNCTION',
'CONTAINS_SQL',
'YES',
'INVOKER',
'_in CHAR(3)',
'CHAR(3)',
'RETURN (SELECT UPPER(_in));',
'Function generated by procedure'
);
Results from running the routines
mysql> CALL test.proc_test('yyy');
+------------+
| UPPER(_in) |
+------------+
| YYY |
+------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT test.func_test('aaa');
+-----------------------+
| test.func_test('aaa') |
+-----------------------+
| AAA |
+-----------------------+
1 row in set (0.00 sec)