I found two very strange problems in MySQL database.
My MySQL database version is 5.6.
Problem 1:
I have simple store procedure for update column value:
Store procedure is as below:
drop PROCEDURE if exists mysql_TestProc;
CREATE PROCEDURE mysql_TestProc(Finaltable VARCHAR(1024),ColTOProcess VARCHAR(1024)
,strFind TEXT,strReplace TEXT)
Label1:BEGIN
DECLARE code VARCHAR(1024) DEFAULT '00000' ;
-- Exception Handler
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE;
END;
-- generate dynamic Query
SET #s:=CONCAT('UPDATE ',FinalTable,' SET
',ColTOProcess,'=REPLACE(',ColTOProcess,',\'',strFind,'\',\'',strReplace ,'\');');
PREPARE stmt from #s;
EXECUTE stmt;
-- If any exeption during query execution then Exception Handler will
-- assign error code to "code" variable
-- else "code" variable will have default value.
IF code != '00000' THEN
-- Error found..
select code ;
LEAVE Label1;
END IF;
END;
call mysql_TestProc("AnyTableName","ColumnName","Find Value","Replace Value").
If you call above store procedure with appropriate parameters it will
successfully update value.
But in my database It successfully update value with error code "42S22".
I changed machine then everything works fine.
So This strange behavior is only with my machine and my database("_temp").
Problem 2:
I have simple procedure as below:
DROP PROCEDURE IF EXISTS mysql_PrepareLogTable;
CREATE PROCEDURE mysql_PrepareLogTable(LogTable VARCHAR(1024),code TEXT,comment TEXT,category VARCHAR(1024),timestamp DATETIME,duration VARCHAR(100),rows INT,msg TEXT)
BEGIN
SET code=CONCAT(comment,' \n ',code);
SET #tempprepare=CONCAT('INSERT INTO ',LogTable,' VALUES ("',code,'","',category,'","',timestamp,'","',duration,'",',rows,',','"',msg,'")');
PREPARE stmt from #tempprepare;
EXECUTE stmt;
END;
I can compile above store procedure script all my mysql databases except one database("test2").
Only in database "test2", I am not able to compile above store procedure script.
Even I drop the store procedure and then try to execute the script but still It failed to compile script in database "test2".
I am using Toad 6 and mysql workbench 6.0.
So, anyone has any idea about these two problems.
Thank You,
Ronak
Related
i am handing over a project to a another party which i have been doing for some time, in this project i do some modifications to some of the existing table by adding coloumns, renaming coloumns etc.
When i was handling the project what i did was, putting the changes or the modifications inside a stored proceedure once it was run calling the function from the query browser.
stored proceedure
CREATE DEFINER=`my_db`#`10.%` PROCEDURE `alter_test_1`()
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_table VARCHAR(100) DEFAULT "";
DECLARE stmt VARCHAR(500) DEFAULT "";
DECLARE column_cursor CURSOR FOR
SELECT TABLE_NAME FROM `information_schema`.`tables` WHERE table_schema = 'my_db'
AND table_name LIKE 'tot_table_%';
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN column_cursor;
alter_tables: LOOP
FETCH column_cursor INTO v_table;
IF v_finished = 1 THEN
LEAVE alter_tables;
END IF;
SET #prepstmt = CONCAT('ALTER TABLE my_db','.',v_table,' CHANGE OS platform VARCHAR(25);');
PREPARE stmt FROM #prepstmt;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP alter_tables;
CLOSE column_cursor;
END
afterwards i run
call alter_test_1();
in the query browser.
Now as im handing this over this two step excution is not professional therefore is there a way for me to do this by using another stored proceddure instead running a call alter_test_1() separately... what i mean is there a way to put this call or several call statements inside a stored proceedure and excecute all the call statements in one shot, once that particular stored proceedure is run.
Well... you call a stored procedure within another stored procedure... by calling it.
CREATE PROCEDURE `do_things`()
BEGIN
CALL `alter_test_1`();
CALL `do_more_stuff`();
END
Executing CALL do_things(); will first run your procedure, then one called "do_more_stuff" and then will return. It will terminate at the first unhandled error thrown unless you catch the error with a HANDLER.
That seems like the answer to the question you asked. I'm less clear about whether that was the question you intended to ask, because you talk about "2 step execution," and I don't see what the 2 steps are.
If you are asking how to run a stored procedure (step 2?) without declaring it first (step 1?), then, no, you can't... it's a "stored" procedure, and has to be stored before it can be executed.
When I create this stored procedure, I get no errors and it gets created just fine.
DELIMITER $$
CREATE DEFINER=`portaluser`#`%` PROCEDURE `sp_CreateDatabaseAndTablesByIdAndName`(
IN p_vendorId VARCHAR(256),
IN p_name VARCHAR(256)
)
BEGIN
SET #db_create = CONCAT('CREATE DATABASE IF NOT EXISTS xx_client_', p_vendorId, '_', p_name);
PREPARE stmt FROM #db_create;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
When I call this stored procedure, it creates the database, yet throws a warning: 1295 This command is not supported in the prepared statement protocol yet.
CALL sp_CreateDatabaseAndTablesByVidAndName('11111', 'xx');
I can't seem to find anywhere online that tells me exactly why this is throwing an error, seeing as though it's a fairly simple query. I've seen other people do essentially the same thing without any problems. I need a second set of eyes to tell me where I'm going wrong. Any help is appreciated.
**Note: I had previously changed p_VendorId to an INT(11) and called it the following way, but still got the same error:
CALL sp_CreateDatabaseAndTablesByVidAndName(11111, 'xx');
Turns out it was because I did this:
END
Rather than:
END $$
DELIMITER ;
Problem
I have a stored procedure:
CREATE PROCEDURE `ProblematicProcedure` (IN dbName varchar(50), IN tableId INT)
MODIFIES SQL DATA
BEGIN
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v2;
CALL ExecuteSql(CONCAT("CREATE VIEW v1 AS SELECT * FROM ",dbName,".my_table;"));
CALL ExecuteSql(CONCAT("CREATE VIEW v2 AS SELECT * FROM ",dbName,".table_",tableId,";"));
...
When called directly from command line or a client like Navicat or HeidiSql, it works well:
CALL ProblematicProcedure("my_schema",1);
But if called from a custom Apache module using the exactly same line above, it crashes on first ExecuteSql call. I have to make it work when called from the Apache module and couldn't find a reason to crash.
ExecuteSql definition
CREATE PROCEDURE ExecuteSql (IN sql_str TEXT)
BEGIN
SET #query = sql_str;
PREPARE stm FROM #query;
EXECUTE stm;
DEALLOCATE PREPARE stm;
END
What I tried?
Swapped two ExecuteSql calls.
Inlined ExecuteSql calls.
Removed ExecuteSql's and used direct SQL statements with hardcoded dbName and tableId values.
Created procedure without MODIFIES SQL DATA.
Granted CREATE VIEW privilege: GRANT ALL ON *.* TO 'myuser'#'%';
Note: I added simple insert statements between the lines to find where it is crashing. So, I am sure it crashes always on first ExecuteSql call.
Question
What can be reason to this crash?
Update: Finally, I managed to find error code:
ERROR 1312: Procedure can't return a result set in the given context
Solution
Use CLIENT_MULTI_STATEMENTS flag when connecting:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
Why this is so?
Calling a stored procedure means executing multiple statements. So, I need to specify that I can execute multiple statements at once. Hence I am using MySql C API functions at client-side (in my Apache module), I need to specify CLIENT_MULTI_STATEMENTS flag when connecting:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
Or set it later:
mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON);
I learnt those from the C API Handling of Multiple Statement Execution page.
How I Debugged?
Debugging a stored procedure is not so easy. I used traditional log-table method, but performed a bit aggresively about finding the error code.
Firstly, defined two variables to keep the code and message about the error occurred:
DECLARE E INT DEFAULT 0; -- error code
DECLARE M TEXT DEFAULT NULL; -- error message
Then, defined possible error codes and messages both for client and server errors (full list here):
DECLARE CONTINUE HANDLER FOR 1000 SET E='1000', M="hashchk";
DECLARE CONTINUE HANDLER FOR 1001 SET E='1001', M="isamchk";
...
...
DECLARE CONTINUE HANDLER FOR 1312 SET E='1312', M="PROCEDURE %s can't return a result set in the given context";
...
...
DECLARE CONTINUE HANDLER FOR 1638 SET E='1638', M="Non-ASCII separator arguments are not fully supported";
DECLARE CONTINUE HANDLER FOR 1639 SET E='1639', M="debug sync point wait timed out";
DECLARE CONTINUE HANDLER FOR 1640 SET E='1640', M="debug sync point hit limit reached";
...
...
DECLARE CONTINUE HANDLER FOR 2057 SET E='2057', M="The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again";
And finally, put logs in critical points:
IF E> 0 THEN
CALL WriteLog(CONCAT("Error ", E, ": ", M));
END IF;
WriteLog is another procedure that only inserts into a log table. This method gave me the error code (1312) and then some Googling worked.
My question may be stupid but I have a 2 days experience in MYSQL. I'm trying to use MyTAP for unit testing and face a problem .
here's the SQL code exucted on a mysql console:
drop procedure IF EXISTS tap.tstTableCount;
delimiter //
CREATE PROCEDURE tap.tstTableCount (IN db CHAR(64), IN tbl CHAR(64))
BEGIN
DECLARE l_sql VARCHAR (4000);
SET l_sql=CONCAT('SELECT COUNT(*)>0 FROM ',db,'.',tbl,';');
SET #sql=l_sql;
PREPARE s1 FROM #sql;
EXECUTE s1;
DEALLOCATE PREPARE s1;
END //
delimiter ;
call tap.tstTableCount('storibo','relationCategory'); /* This call works fine and returns 1 (true)*/
SELECT tap.ok(
tap.tstTableCount('storibo','relationCategory'),
'relationCategory contains data'
); /* this one returns :
ERROR 1305 (42000): FUNCTION tap.tstTableCount does not exist */
is it an issue with the MyTAP fmk or do I make a mistake in my syntax ?
The problem is your tap.tstTableCount is a PROCEDURE and not a FUNCTION. All the MyTAP tests are are FUNCTIONS, as noted by being able to call it in a SELECT statement. You cannot call a PROCEDURE from a SELECT statement, instead needing to call it via:
CALL tap.tstTableCount();
Unfortunately, your example is one that cannot simply be converted to a FUNCTION to be used by the mytap functions. Dynamic SQL is not permitted in FUNCTION, but is allowed in PROCEDURE.
Is it possible to create a trigger in MySQL using dynamically generated SQL from within a stored procedure? I am executing other dynamically constructed queries in my procedure by preparing a statement, but when I try the same approach to create a trigger I get the following error:
ERROR Code: 1295This command is not supported in the prepared statement protocol yet
From Bug #31625, PREPARED STATEMENT syntax does not allow to create TRIGGERS I see other people have been complaining about the same thing since 2007.
And from the look of WL#2871: Prepare any SQL it has not yet been fixed.
Is there a workaround for this problem? Is there another way of creating triggers with dynamic SQL?
Basically what I am trying to do is dynamically create triggers for recording audit data for inserts on various different tables. I am listing the tables I want to audit in an *audit_tables* table. The stripped-down procedure below iterates over the entries in that table and tries to create the trigger.
drop procedure if exists curtest;
delimiter |
create procedure curtest()
BEGIN
DECLARE done INT DEFAULT 0;
declare tn varchar(16);
declare cur cursor for select table_name from audit_tables;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
fetch cur into tn;
if done then
leave read_loop;
end if;
/* Create the BEFORE INSERT trigger */
set #sql = concat('CREATE TRIGGER audit_', tn, '_bi BEFORE INSERT ON ', tn, '
FOR EACH ROW BEGIN
set new.foo="bar";
END;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end LOOP;
close cur;
END;
|
delimiter ;
call curtest();
As the error you mention says, the CREATE TRIGGER command is not supported within prepared statements.
I think a more viable option is to use a scripting language that has MySQL bindings, like PHP, to automate the trigger creation. By the way, I just remembered that MySQL Workbench uses Lua as a scripting language for this sort of things.