I am creating sort of auditing functionality for all of my stored procedures. I am able to fetch the name of the parameters that stored procedure has(from : information_schema.parameters table).
However, I would like to create generic code for all stored procedures that would fetch the name of the parameters and correspondingly get the value of those parameters for that invocation and log into another table.
e.g.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `TestSP`(IN `name` VARCHAR(255), IN `userid` INT(255), IN `isnew` VARCHAR(11))
BEGIN
#DECLARE exit handler for sqlexception ROLLBACK;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
INSERT INTO app_db_log(TYPE,MESSAGE, INFO) VALUES ('ERROR','An error has occurred, operation rollback and the stored procedure was terminated',<INSERT ALL THE PARAMETER VALUES AS SINGLE STRING HERE> );
COMMIT;
SELECT 'An error has occurred, operation rollback and the stored procedure was terminated';
END;
START TRANSACTION;
SIGNAL SQLSTATE '45000';
COMMIT;
END$$
DELIMITER ;
Thanks in advance!
You can get the list of parameters for the routine from INFORMATION_SCHEMA.PARAMETERS, but you'd have to know the schema and name of the procedure:
mysql> delimiter $$
mysql> create procedure myproc (in foo int, in bar int)
-> begin
-> select group_concat(parameter_name order by ordinal_position) as params
-> from INFORMATION_SCHEMA.PARAMETERS
-> where specific_schema='test' and specific_name='myproc';
-> end$$
mysql> call myproc(123, 456)$$
+---------+
| params |
+---------+
| foo,bar |
+---------+
You would need to use dynamic SQL to do this, but you can't reference stored proc parameters in dynamic SQL:
Demonstration:
mysql> create procedure myproc (in foo int, in bar int)
-> begin
-> set #sql = 'SELECT foo, bar';
-> prepare stmt from #sql;
-> execute stmt;
-> end$$
mysql> call myproc(123, 456)$$
ERROR 1054 (42S22): Unknown column 'foo' in 'field list'
I generally discourage people from using MySQL stored procedures. This task would be far easier in virtually any application programming language.
Related
I am using MySQL -actually MariaDB from PHPMyAdmin- and trying to write an insert inside a stored procedure and for obvious (security) reasons it is not allowed.
I tried to change the permissions using the GRANT EXECUTE ON PROCEDURE statement.
GRANT EXECUTE ON PROCEDURE test.putDataInFull TO 'root'#'localhost'
I have really hit a wall here, any ideas?
edit:
DELIMITER //
CREATE PROCEDURE putDataInFull (IN matchid INT(11))
BEGIN
DECLARE koula int(11);
DECLARE c varchar(255);
SET #koula = matchid;
SET #c := concat('insert into log (match_id, comment) values (?,\'inPUtDataWeTrtust\');');
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT CONCAT(#c, ' is not valid');
END;
PREPARE stmt FROM #c;
EXECUTE stmt USING #koula;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
p.s. this is not a production project, just fun, so I really do not care about security.
You can create an insert with your variables. It is not necessary to create a magic string. Here you get an other example of an other stackoverflow question:
DELIMITER $$
CREATE PROCEDURE ADD_WITHDRAWAL_A(IN withdrawalcode_p VARCHAR(25), IN id_p VARCHAR(8), IN amount_p VARCHAR(12), IN datewithdrawn_p VARCHAR(35), IN approved_p VARCHAR(8))
BEGIN
START TRANSACTION;
INSERT INTO Withdrawals(WithdrawalCode, IDD, Amount, DateWithdrawn, Approved)
VALUES (withdrawalcode_p, id_p, amount_p, datewithdrawn_p, approved_p);
UPDATE account SET AccountBalance = AccountBalance - amount_p WHERE IDD = id_p LIMIT 1;
COMMIT;
END $$
DELIMITER ;
I'm using sybase powerbilder12 IDE and mySQL.
I have a stored procedure like this:
DELIMITER //
CREATE PROCEDURE CRTempTable(IN loc_code CHAR(6))
BEGIN
create temporary table mstparameter (select * from mstparameter_consolidate where location_code = 'loc_code');
END//
DELIMITER ;
I'm calling it in the powerbuilder12 like this:
DECLARE TempTBCRCall PROCEDURE FOR TempTableCR
location_code = :gs_location_code_mstparameter ;
execute TempTBCRCall;
It gives me the error :
Stored procedure execution failure1054 SQLSTATE = S0022
[MySQL][ODBC 5.2(a) Driver][mysqld-5.5.25a]Unknown column
'location_code' in 'field list'... Error Code 0
but location_code is there in my mstparameter_consolidate table.
If I set to enter the location_code manually it works fine.
This is an example that works, I hope it helps you.
DECLARE pb_acceso_usuario PROCEDURE FOR SP_ACCESO_VALIDA_DATOS_USUARIO (:gs_cod_usuario,:ls_password);
execute pb_acceso_usuario;
if SQLCA.sqlcode = 0 then
FETCH pb_acceso_usuario INTO :ln_count,:gs_des_usuario,:ls_estado;
CLOSE pb_acceso_usuario;
end if
try putting "table-name." in front of the column-name.
For some strange reason, inserting from stored procedure is not working.
This is what Im trying to do:
DROP TABLE IF EXISTS test;
CREATE TABLE IF NOT EXISTS test(
id INT(9) NOT NULL AUTO_INCREMENT
,name VARCHAR(30) NOT NULL
,PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
insert into test (name) values('A');
Inserting from command line works with no problems.
Then I created a stored procedure to do the same kind of insert:
DROP PROCEDURE IF EXISTS storedtest;
DELIMITER $$
CREATE PROCEDURE storedtest()
BEGIN
declare insert_sql varchar(200);
SET insert_sql = 'insert into test (name) values(3)';
SELECT insert_sql;
PREPARE mystm FROM #insert_sql;
EXECUTE mystm;
END$$
DELIMITER ;
call storedtest();
This gives me the error:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'NULL' at line 1
NULL? Where did NULL came from?
I also tried changing the sql-insert to look like this (dont know if it is a good way):
SET insert_sql = "insert into test (name) values('3')";
But mysql gives me exactly the same error.
Anyone has a clue?
The NULL MySQL is reporting is an empty user variable #insert_sql, which is different from the local stored procedure local variable insert_sql which you allocated with DECLARE.
MySQL's DECLARE is used for variables local to a stored program, but according to the documentation, PREPARE stmt FROM ... expects either a string literal or a user variable, which are the type preceded with #.
PREPARE stmt_name FROM preparable_stmt
preparable_stmt is either a string literal or a user variable that contains the text of the SQL statement.
You can allocate the untyped user variable with SET so there is no need for DECLARE. You may wish to set it to NULL when you're finished.
DROP PROCEDURE IF EXISTS storedtest;
DELIMITER $$
CREATE PROCEDURE storedtest()
BEGIN
-- Just SET the user variable
SET #insert_sql = 'insert into test (name) VALUES (3)';
SELECT #insert_sql;
-- Prepare & execute
PREPARE mystm FROM #insert_sql;
EXECUTE mystm;
-- Deallocate the statement and set the var to NULL
DEALLOCATE PREPARE mystm;
SET #insert_sql = NULL;
END$$
DELIMITER ;
I have a issue when I try to create a procedure.
First with :
DROP TABLE IF EXISTS curr_rate;
There no problem the table is dropped.
Now with a procedure :
CREATE DEFINER=`root`#`localhost` PROCEDURE `Drop_table_Gen`(IN tname varchar(20))
BEGIN
SET #query = CONCAT('DROP TABLE IF EXISTS `',tname,'`');
PREPARE stm FROM #query;
EXECUTE stm;
END
I get the following error:
error code 1054 Unknown column
The same issue if I write :
CREATE DEFINER=`root`#`localhost` PROCEDURE `drop_table_gen2`(IN tname varchar(20))
BEGIN
DROP TABLE IF EXISTS tname;
END
I hope someone has an idea about this problem
Thanks
First of all, you are calling like call Drop_table_Gen(curr_rate);. shouldn't you be quoting the parameter like call Drop_table_Gen('curr_rate'); since it's a VARCHAR type.
If still same error then I don't see anything wrong with the way you are trying to drop the table. To my believe, somehow IF EXIST is creating the problem here. Try without that (below procedure) and it will work.
CREATE PROCEDURE `Drop_table_Gen`(IN tname varchar(20))
BEGIN
SET #query = CONCAT('DROP TABLE `',tname,'`');
PREPARE stm FROM #query;
EXECUTE stm;
END
If you are using MySQL 5.5 or above then you can use EXIT HANDLER for error catching within your procedure like below
CREATE PROCEDURE `Drop_table_Gen`(IN tname varchar(20))
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SET #error_count = #error_count + 1;
IF #a = 0 THEN RESIGNAL; END IF;
END;
BEGIN
SET #query = CONCAT('DROP TABLE `',tname,'`');
PREPARE stm FROM #query;
EXECUTE stm;
END
Then while calling the procedure
SET #error_count = 0;
SET #a = 0;
CALL Drop_table_Gen('curr_rate');
So in case, table doesn't exist it will pop up the SQL error
ERROR 1051 (42S02): Unknown table 'curr_rate'
See a detailed document here RESIGNAL Syntax
I've written a stored procedure. It's working fine except taking the table name as input parameter.
Let see my proc in MySQL:
DELIMITER $$
USE `db_test`$$
DROP PROCEDURE IF EXISTS test_proc$$
CREATE DEFINER=`root`#`localhost`
PROCEDURE `test_proc`(IN serviceName VARCHAR(10),IN newsInfoTable VARCHAR(100))
BEGIN
SELECT COUNT(*) FROM newsInfoTable WHERE newsServiceName=serviceName;
END$$
DELIMITER ;
Stored procedure calling parameters:
USE db_test;
CALL test_proc('abc','tbl_test_news');
Here the service name parameter is working fine. But if I include the newsInfoTable variable as table input parameter then a error shows.
Table 'db_test.newsinfotable' doesn't exist
Why does this happen only for table parameter? How can I retrieve from this error or
How I pass a table name into a stored procedure as a parameter?
An SP cannot be optimized with a dynamic table name, so many DBs, MySQL included, don't allow table names to be specified dynamically.
One way around this is to use Dynamic SQL.
CREATE DEFINER=`root`#`localhost` PROCEDURE `test_proc`(IN serviceName VARCHAR(10),IN newsInfoTable VARCHAR(100))
BEGIN
SET #sql = CONCAT('SELECT COUNT(*) FROM ',newsInfoTable,' WHERE newsServiceName=?;');
PREPARE s1 from #sql;
SET #paramA = serviceName;
EXECUTE s1 USING #paramA;
END$$
You can use EXECUTE IMMEDIATE for a "less is more" solution (for me, less code = good)
CREATE PROCEDURE test_proc(IN serviceName VARCHAR(10), IN newsInfoTable VARCHAR(100))
BEGIN
EXECUTE IMMEDIATE CONCAT('SELECT COUNT(*) FROM ',newsInfoTable,' WHERE newsServiceName=''', serviceName, '''');
END
that part of a query cannot be dynamic.
you may consider implementing as a string that is executed dynamically at runtime
Although may not be what you want, alternatively, can consider to use conditionally if and prepare the statement.
DELIMITER $$
CREATE PROCEDURE select_count(IN table_name VARCHAR(20))
BEGIN
IF table_name = 'xxx' THEN
SELECT * FROM xxx;
ELSEIF table_name = 'yyy' THEN
...
ENDIF
END$$