IF-ELSE Not Working While Calling a Procedure - mysql

I am trying to call a procedure with a MySQL command as an argument. Everything is working perfect, but last procedure call where there has been implemented a conditional statement is behaving badly. The error description encountered when the procedure is called is provided below:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IF #CheckExists > 0 THEN
set #result= ('done')
For more information, here i am passing a statement that should have been implemented itself in a procedure (i guess, loops, cursors conditions has to be defined in a procedure in MySQL) and hence an error. But i could not figure this out as the database name (#dbname in this case) has to be passed dynamically and even i tried to make another procedure to make this work i end up with the same error.
Any help would be greatly appreciated.
Thanks
use test;
drop procedure if exists test;
set #dbname = 'test_qa';-- only for the sake of example. this is to be dynamically done
delimiter //
create procedure test(IN sqlCommand text)
begin
SET #tquery = sqlCommand;
PREPARE stmt FROM #tquery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
end//
delimiter ;
CALL test (concat('SET #CheckExists =(select COUNT(*) from ',#dbname,'.course
where Name = ''Computing'' and Value = ''True'')'));
-- call test ('select #CheckExists');
CALL test (concat('IF #CheckExists > 0 THEN
set #result = (''done'')
else
set #result = (''gone'')
end if'));

You can't use IF (or procedural code at all) in anonymous blocks in MySQL.
Use this:
CALL test('SET #result = CASE WHEN #checkExists > 0 THEN ''done'' ELSE ''gone'' END')

Related

MySql prepare statement - is it possible to parametrize column name or function name?

Lets say that I want to write a procedure allowing me to call certain function on certain column, for example:
call foo('min','age') -> SELECT min(age) FROM table;
I want my procedure to be safe from sql injection, therefore, I'm willing to use prepared statements and parametrize the input
SET #var = "SELECT ?(?) FROM table;"
PREPARE x FROM #var;
EXECUTE x USING a, b;
Where a and b are input parameters, function and column, respectively.
However, it doesnt seem to be possible - InnoDB keeps throwing an error whenever I want to execute this statement.
Is it possible to solve this this way, or I need to resort to whitelisting?
EDIT:
Full code:
create procedure test(in func varchar(20), in col varchar(20))
begin
set #f = func;
set #c = col;
set #sql = "select ?(?) from table;";
prepare x from #sql;
execute x using #f, #c;
end;
calling:
call test('min','age');
Full error:
[42000][1064] You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the right syntax to
use near '(?) from table' at line 1
You cannot parametrize column/table/function name/alias. As, PREPARE statement only allow "values" part of the SQL query to be used as parameters. Function/Table/Column name/alias are used to determine the validity of the SQL statement; and thus cannot be changed during run-time execution. Changing it at execution time would potentially alter whether the SQL statement was valid.
You can think of it as compiling a code; hence the compiler must know all the function/class name(s) etc for creating a valid executable (yes, we can do dynamic classes, but that is rare). On the other hand, we can change input "values" to the program, but generally cannot change the operations to be done on the input data.
Also, MySQL server would consider the parameters as literals, and apply quotes around them, before using them in query execution.
Now, in your case, you can still use the function name as parameter for Stored procedure, and generate the query string using that. But you cannot use it as a parameter for the query itself.
delimiter $$
create procedure test(in func varchar(20), in col varchar(20))
begin
set #c = col;
-- use concat function to generate the query string using func parameter
set #sql = concat('select ', func, '(?) from table');
-- prepare the statement
prepare stmt from #sql;
-- execute
execute x using #c;
-- don't forget to deallocate the prepared statement
deallocate prepare stmt;
end$$
delimiter ;
I came across this while writing a mySQL query builder plugin. My solution was to prefix column and function names with a "?" character (the user can change the character in the plugin preferences).
The code that builds the prepared statement looks for values that begin with "?" and inserts the subsequent column/function name into the query inline instead of as prepared statement values.

Using procedure variables to create server-side prepared statements — MySQL

Within procedures, why does using session-specific variables to store string queries work:
DELIMITER $$
CREATE PROCEDURE `test_prepared_stmt` () BEGIN
SET #q:="SELECT * FROM t1";
PREPARE query FROM #q;
#...
END$$
but using procedure variables not:
DELIMITER $$
CREATE PROCEDURE `test_prepared_stmt` () BEGIN
DECLARE q TEXT;
SET q:="SELECT * FROM t1";
PREPARE query FROM q;
#...
END$$
and instead throw the following error?
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'q;
END' at line 5
Is this just a quirk of the language?
You need to use SQL standard variable syntax - all variables are declared/used with the # symbol.
So, in your example, if you used:
declare #q text
or,
declare #q nvarchar(4000)
or anything similar, it will work - just follow the SQL spec for usage of variables and you should be fine, regardless of whether you are within a procedure or passing a parameter, or anything else.

Mysql very strange behavior

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

In mysql dynamic stored proc, is #var required to build a prepared statement?

I have a similar stored proc (but longer). It is called from PHP (GET request on Apache)
delimiter //
CREATE PROCEDURE dynamic(IN tbl CHAR(64), IN col CHAR(64))
BEGIN
SET #full_statement = CONCAT('SELECT ',col,' FROM ',tbl );
PREPARE stmt FROM #full_statement;
EXECUTE stmt;
END
//
delimiter ;
From what I read, #s is a mysql session variable living as long as my session is alive.
The #s presence annoys me since I fear that 2 concurrent request on that stored
proc might play with this "global variable". So I tried to remove the '#' like this
delimiter //
CREATE PROCEDURE dynamic(IN tbl CHAR(64), IN col CHAR(64))
BEGIN
DECLARE full_statement VARCHAR(300);
SET full_statement = CONCAT('SELECT ',col,' FROM ',tbl );
PREPARE stmt FROM full_statement;
EXECUTE stmt;
END
//
delimiter ;
to build the prepared statement without success.
I seem to have constantly
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near 'full_statement; EXECUTE stmt;
Does my fear of 2 calls within my php session is really a problem ?
(If not, what about having 200 stored procedures using that same
global variable) ?
Can I really achieve my goal and remove the '#' and let the prepared
statement being handle by a simple stored proc variable or is it a constraint of prepared statement ?
Regards.
Yes, the #var is required.
MySQL's PREPARE statement only accepts "user variables" (those prefixed with #), not local variables declared in a stored routine.
This has long been recognized as a WTF in MySQL:
Bug #17409 PREPARE doesn't support queries in local variables
Does my fear of 2 calls within my php session is really a problem ?
No. It's not a global variable, it's a session variable.
Two concurrent sessions have their own value for #var.

MYSQL MyTAP issue

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.