Prepared IF block in stored procedure - mysql

I was testing out MySQL's PREPARE stmt FROM preparable_stmt; functionality in a stored procedure and decided to try out control blocks, starting with IF:
DELIMITER $$
CREATE PROCEDURE `testif`(t TEXT) BEGIN
DECLARE q TEXT;
SET #q:=CONCAT("IF (SELECT COUNT(*) FROM ", t, ")>5 THEN");
PREPARE query FROM #q; EXECUTE query;
DEALLOCATE PREPARE query;
SELECT "That's a nice table!";
INSERT INTO ... #and other behaviour other than SELECT
SET #q:="END IF";
PREPARE query FROM #q; EXECUTE query; DEALLOCATE PREPARE query;
END$$
Since it was being executed in the context of a stored procedure, I kind of expected this to work. Instead, when I CALL testif("t1");, I get this syntax 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 'IF (SELECT COUNT(*) FROM t1)>5 THEN' at line 1
which I would get if I ran the query outside of the procedure.
For the record, I know how to make this query work without PREPAREing the IF clause directly. My question is purely out of curiosity, since I'm used to how eval-like functions work in other programming languages (where it executes the string as if it were hard-coded on that line). Setting variables and most other things work with PREPARE, but then again, they'd work outside of the procedure as well. Why doesn't this?

Related

Mysql Stored Procedure: match the column rule to retrieve value

I have a stored procedure which function to retrieve the ColumnProperty based on the input parameter matching the ColumnRule.
For example,
I will execute the below to get the ColumnProperty as string(short). But the problem is when using WHERE to filter ColumnRule, the retrieved value is string and get unknown type at the end.
CALL `testing-spGetColumnType`('varchar(11)', #outputProperty);
select #outputProperty;
DELIMITER $$
CREATE PROCEDURE `testing-spGetColumnType`(IN pColumnType varchar(50),OUT pColumnProperty varchar(50))
BEGIN
SELECT ColumnProperty FROM model_column_type where pColumnType like replace(ColumnRule, 'variable', pColumnType) into pColumnProperty;
SELECT IFNULL(pColumnProperty,'unknown type') into pColumnProperty;
END$$
DELIMITER ;
The sample table:
when the condition in the WHERE clause is evaluated against the stringShort row, given 'varchar(11)' as the argument, it's equivalent to
WHERE 'varchar(11)'
LIKE '%varchar% and SUBSTRING_INDEX(SUBSTRING_INDEX(varchar(11),''('',-1),'')'',1) < 50'
and the result of the LIKE comparison will be FALSE.
The value of ColumnRule from that row
%varchar% and SUBSTRING_INDEX(SUBSTRING_INDEX(variable,'(',-1),')',1) < 50
is a value. It doesn't matter that it looks like SQL text. In the context of the SELECT statement, it is just a string of characters. It's a string value. It is not references to identifiers, or SQL functions, or boolean operators.
We would get the same result if the ColumnRule value was
%varchar% one two buckle my shoe
To get a string value to be seen as SQL text, we can use dynamically prepared SQL.
In the context of MySQL PROCEDURE, we can dynamically create SQL text and store it as a string, and then execute the string, something like this:
SET #foo = ' foo, bar' ;
SET #sql = CONCAT('SELECT ',#foo,' FROM mytab',' ORDER BY ',#foo);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
A word of caution: if we dynamically create SQL text, and incorporate potentially unsafe values, we can open a gaping SQL Injection vulnerability, ala Little Bobby Tables https://xkcd.com/327/
MySQL Reference https://dev.mysql.com/doc/refman/8.0/en/sql-syntax-prepared-statements.html

Why can't I prepare this sql statement to set auto_increment?

I want to set the auto_increment value for each column by first finding the max id value.
I am referencing the code from this SO question.
The mysql docs for prepared statements show a similar format, so I am confused.
When I try running the prepare statement I get a failure. Why?
Below is the output when I try to prepare a regular statement and then when I try to prepare the auto_increment statement with a '?' for binding later.
mysql> PREPARE stmt1 FROM 'ALTER TABLE user AUTO_INCREMENT=2';
Query OK, 0 rows affected (0.00 sec)
Statement prepared
mysql> PREPARE stmt1 FROM 'ALTER TABLE user AUTO_INCREMENT=?';
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 '?' at line 1
An alternative would be
set #alter_statement = concat('alter table user auto_increment = ', #value);
prepare stmt1 from #alter_statement;
execute stmt1;
deallocate prepare stmt1;
For some reasons it seems many people are experiencing a syntax error when using prepare with ?.
My guess is that it fail because the given value will be replaced with the value, between two single quotes (this is just a guess though).

MySQL create a dynamic table name in a Join statement

long time user, first time poster.
I have 2 tables;
a1_watchlists {id(PK),name,date}
a1_watchlist {id(PK),watchlists_id(FK(a1_watchlists.id)),company_name,asx_code,date}
I also have 2000 other tables that have been created with the name 'asx_'+[asx_code] (where asx_code is pulled from another table)
this table looks like;
asx_[asx_code] {date(PK),open,high,low,close,volume}
I want to select all from a1_watchlists and a1_watchlist and then select the latest date from the asx_[asx_code] table using the value from a1_watchlist.asx_code to generate the [asx_code] part of the table name.
The problem I have is that I want to use the value from a1_watchlist.asx_code as the table name prepending the string 'asx_' to this first.
Closest I have been able to get is;
DECLARE #TableName VARCHAR(100)
SELECT *
FROM a1_watchlist AS wl
JOIN a1_watchlists AS wls
ON wls.id = wl.watchlists_id
SET #TableName = 'asx_' + wl.asx_code
INNER JOIN (SELECT MAX(date),open,high,low,close,volume,amount_change,percent_change FROM #TableName)
This currently give the error:
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 'DECLARE #TableName VARCHAR(100)
SELECT *
FROM a1_watchlist AS wl
' at line 1
The expected colums I need in the final result would be:
wl.id,wl.watchlists_id,wl.company_name,wl.asx_code,asx_[asx_code].date,asx_[asx_code].open,asx_[asx_code].high,asx_[asx_code].low,asx_[asx_code].close,asx_[asx_code].volume
Let me know if you require more information.
I'm not going to speak to what to do in the case where you have 2000+ tables that start with asx+ some code... (i live in a town with multiple bridges) or even whether what you're doing is the best way to get where you want to go. BUT, it does look like you're attempting to concatenate things together and create a dynamic statement. If that sounds right, then I'd recommend you look into prepared statements. Like the following. Hope this helps.
DELIMITER $$
DROP PROCEDURE IF EXISTS prRetrieveAllFromTable$$
CREATE PROCEDURE prRetrieveAllFromTable(tableName VARCHAR(64))
BEGIN
SET #s = CONCAT('SELECT * FROM ',tableName );
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
CALL prRetrieveAllFromTable('calendar');
http://dev.mysql.com/doc/refman/5.5/en/sql-syntax-prepared-statements.html
How To have Dynamic SQL in MySQL Stored Procedure

How to OUT from a stored procedure using LAST_INSERT_ID()

Mysql Version 14.14 Distrib 5.1.41
I have a stored procedure that I am trying to get compiled but an error keeps cropping up. My stored procedure prepares an insert statement, executes it, deallocates the prepared statment and then attempts to return the last insert id.
This is an pseudo extract:
1 CREATE PROCEDURE `audit`( IN pExampleValue varchar(50))
2
3 BEGIN
4 PREPARE stmt FROM 'INSERT INTO `Audit` (`Value`) VALUES (?)';
5 EXECUTE stmt USING #pExampleValue;
6 DEALLOCATE PREPARE stmt
7
8 RETURN LAST_INSERT_ID() AS `AuditId`;
9 END$$
From what I am able to gather my version of mysql can used prepared statements in stored procedures. However the error I am getting is.
SQL Error (1313): RETURN is only allowed in a FUNCTION
As the error says I cannot return from a procedure. However I can out from a procedure? I'm having a difficult time finding examples of a query being OUT'ed, How exactly would I do that from this example.
Either, declare an OUT parameter, or make your procedure a FUNCTION.
You'll find sufficient documentation here:
http://dev.mysql.com/doc/refman/5.5/en/create-procedure.html

Can i use parameter for mysql "limit start, count"

I want to use stored procedure parameter as start and count in MySQL limit. But it seems limit only accepts constant values. How can i construct a sql in which start and limit is stored procedure parameter?
I really dont know, but I just feel like this will work for you.
[Untested]
DELIMITER $
CREATE PROCEDURE `tmp`()
BEGIN
PREPARE STMT FROM "SELECT * FROM yourTable LIMIT ?,?";
END$
DELIMITER;
SET #a=2;
SET #b=1;
CALL tmp();
EXECUTE STMT USING #a, #b;
You can also set the limit, offset and so on as a Java Prepared Statement parameter. Just try it out. (We're talking about Java, right?)