Join 2 strings as query in a procedure - mysql

How can I construct a query based on parameters I receive? I want to add to the end of the query.
I tried something like this but it didn't work:
DELIMITER $$
CREATE PROCEDURE `get_users`(IN sortstring TEXT)
BEGIN
PREPARE statement FROM
"SELECT username, password FROM users ?";
SET #param = sortstring;
EXECUTE statement USING #param;
END$$
And I would pass to sortstring something like:
ORDER BY username DESC
Can I do this simpler by using concat or something?

You need to deconstruct the sortstring and check all its parts against a whitelist of allowed terms.
See the following pseudo code. I haven't fully tested it, but lets say it's the idea that counts.
DELIMITER $$
CREATE PROCEDURE `get_users`(IN sortstring TEXT)
BEGIN
//check sortstring against a whitelist of allowed sortstrings
DECLARE sortpart VARCHAR(255);
DECLARE done BOOLEAN DEFAULT false;
DECLARE allok BOOLEAN DEFAULT true;
DECLARE i INTEGER DEFAULT 1;
WHILE ((NOT done) AND allOK) DO
SET sortpart = SUBSTRING_INDEX(sortstring,',',i);
SET i = i + 1;
SET done = (sortpart IS NULL);
IF NOT DONE THEN
SELECT 1 INTO allok WHERE EXISTS
(SELECT 1 FROM whitelist
WHERE allowed_sort_claused = sortpart AND tablename = 'users');
END IF
END WHILE;
IF allOK THEN
PREPARE statement FROM
CONCAT('SELECT username, passhashwithsalt FROM users ',sortstring);
EXECUTE statement;
DEALLOCATE PREPARE statement;
ELSE SELECT 'error' as username, 'error' as passhashwithsalt;
END IF;
END$$
See: How to prevent SQL injection with dynamic tablenames?
The error in your code
You cannot use columnnames or SQL-keywords as parameters. You can only use values as parameters. For that reason your query will never pass prepare.
The ? in SELECT x FROM t1 ? wil just be replaced by SELECT x FROM t1 'ORDER BY field1, field2' Which makes no sense.

Related

How to return multiple rows in stored procedure?

I have a procedure that returns multiple rows, but separately. Please take a look at its result:
I causes some issues when I want to fetch the result in the code (backend side). Now I want to create a temporary table and insert all rows inside it and then return that temp table as the result of the stored procedure. How can I do that inside procedure?
Not sure it above idea is a good idea .. that's the only thing I can probably be useful to merge all rows all in one table as SP's result.
Here is my current procedure:
DELIMITER $$
CREATE DEFINER=`administrator`#`localhost` PROCEDURE `lending_ewallets_balance_in_merchant`(IN `user_id_param` BIGINT UNSIGNED, IN `business_id_param` INT UNSIGNED)
NO SQL
BEGIN
DECLARE dossier_id INT;
DECLARE query_string VARCHAR(255) DEFAULT '';
DECLARE cursor_List_isdone BOOLEAN DEFAULT FALSE;
DECLARE user_dossiers CURSOR FOR
Select ld.id, lwp.query_string
FROM lending_users_dossiers ld
JOIN lending_where_to_pays lwp ON ld.lending_where_to_pay_id = lwp.id
WHERE user_id = user_id_param
AND (ld.status = 'activated' OR ld.status = 'finished');
# 'finished' is for loans
DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_List_isdone = TRUE;
Open user_dossiers;
loop_List: LOOP
FETCH user_dossiers INTO dossier_id, query_string;
IF cursor_List_isdone THEN
LEAVE loop_List;
END IF;
SET #qry = CONCAT(
"SELECT ld.id lending_dossier_id, ld.type, SUM(let.credit) balance
FROM lending_users_dossiers ld
JOIN lending_ewallet_transactions let
ON ld.id = let.lending_dossier_id
WHERE ld.id = ", dossier_id,
" AND ", business_id_param, " IN(", query_string, ")",
"GROUP BY ld.id, ld.type");
PREPARE stmt FROM #qry;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP loop_List;
Close user_dossiers;
END$$
DELIMITER ;
Noted that, the MySQL version I use is MySQL v8.0.20.
The logic should be something like this. Outside the loop create a temp table if not exists and delete the data from it:
CREATE TEMPORARY TABLE
IF NOT EXISTS
user_dossiers_tmp (your columns);
DELETE FROM user_dossiers_tmp;
In your loop:
INSERT INTO user_dossiers_tmp VALUES (your data);
After your loop:
SELECT * FROM user_dossiers_tmp;
END$$

MySQL Stored Prcedure Debugging Output

So I've written a fairly simple MySQL stored procedure to retrieve values from a database for a personal app that I'm building. From everything I can see, the procedure should work just fine, but it's returning the wrong results.
Here's the procedure code:
USE randyrip_kdb;
DROP PROCEDURE IF EXISTS spGetAllTracksSong;
DELIMITER //
CREATE PROCEDURE spGetAllTracksSong(IN startRecord INT, IN rowsReturned INT, IN searchArtist VARCHAR(255), IN searchTitle VARCHAR(244), IN orderBy VARCHAR(20), IN duets TINYINT(1))
BEGIN
DECLARE spStart INT;
DECLARE spRows INT;
DECLARE whereClause VARCHAR(255) DEFAULT '';
DECLARE whereArtist VARCHAR(255) DEFAULT '';
DECLARE whereSong VARCHAR(255) DEFAULT '';
DECLARE outputSQL VARCHAR(1000) DEFAULT '';
SET spStart=startRecord;
SET spRows=rowsReturned;
IF searchArtist!='' THEN SET whereArtist= CONCAT('artist LIKE \'%',searchArtist,'%\' '); END IF;
IF searchTitle!='' THEN SET whereSong= CONCAT('song_title LIKE \'%',searchTitle,'%\' '); END IF;
IF whereArtist != '' && whereSong !='' THEN SET whereClause=CONCAT('WHERE ', whereArtist,'AND ',whereSong);
ELSEIF whereArtist !='' THEN SET whereClause= CONCAT('WHERE',whereArtist);
ELSE SET whereClause = CONCAT('WHERE',whereSong);
END IF;
IF duets=1 && whereClause !='' THEN SET whereClause=CONCAT(whereClause,' AND is_duet=1');
END IF;
SET orderBy = IFNULL(orderBy, 'song_title');
IF orderBy='date' THEN SET orderBy='date_added DESC'; END IF;
/*select whereClause;
select orderBy;
select startRecord;
select rowsReturned;*/
SET outputSQL=CONCAT('SELECT song_title, artist, comments, disc_number FROM track ', whereClause,'ORDER BY ' ,orderBy,' LIMIT ' ,spStart,',',spRows);
SELECT outputSQL;
SELECT song_title, artist, comments, disc_number FROM track whereClause ORDER BY orderBy LIMIT spStart,spRows;
END//
DELIMITER ;
I'm calling the Stored Procedure with these parameters:
call spGetAllTracksSong(0,20,'elvis costello','peace, love','date',0);
The variable outputSQL is correctly generating the query I want, and when I run it it's returning two rows as expected. However, the procedure itself is returning 20 rows, none of which match the criteria.
If anyone has any ideas as to what I'm doing incorrectly, that would be great. From all that I can see, everything should be fine however.
Randy,
if you use variables in the SQL query (like "FROM track whereClause"), you need to execute with EXECUTE, otherwise it will not be evaluated. Replace your last select with this:
set #sql = outputSQL;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Alternatively, you could try not to use dynamic SQL.

Create SQL Query in function or PROCEDURE

I am trying to create function inside MySQL to create several queries for example :
DELIMITER |
CREATE PROCEDURE queryBuilder(_tableName varchar(100))
BEGIN
SET #str_query = 'SET #countRows = 0;SELECT COUNT(*) INTO #countRows FROM';
SET #str_query = CONCAT(#str_query,_tableName, '; SELECT #countRows');
PREPARE stmt1 FROM #str_query;
EXECUTE stmt1;
END|
DELIMITER;
but it doesn't work? how can i change to work? I know i can create IF statement but i want something with more flexibility.
I think you need to declare that variable before you set it. (I also chose not to use a reserved word in MySQL, i.e. 'count')
Like this (truncated):
BEGIN
DECLARE rowCount INT;
SET rowCount = 0;
SELECT COUNT(*) INTO rowCount from _tableName;
RETURN rowCount;
...
See examples like this: http://www.mysqltutorial.org/mysql-stored-function/
Hope that helps

MySQL - function which returns execute value

I wrote this function:
delimiter //
CREATE FUNCTION randomDefVal(val varchar(30), tableName varchar(30))
returns varchar(30)
BEGIN
SET #query = concat('SELECT ',val,' FROM ',tableName,' ORDER BY rand() LIMIT 1;');
SET #result = NULL;
PREPARE stmt1 FROM #query;
return (EXECUTE stmt1);
END//
But I have an error in the last line:
SQL Error (1336): Dynamic SQL is not allowed in stored function or trigger
Which suggests that I cannot write 'return (EXECUTE stmt1);'
How can I return the value, which will be the result of the 'EXECUTE' statement?
I think what you want is SELECT ... INTO. So you would have something like this within your BEGIN and END (note that I have not tested this code):
BEGIN
DECLARE var_name VARCHAR(30);
SET var_name = '';
SELECT val INTO var_name FROM tableName ORDER BY rand() LIMIT 1;
RETURN var_name;
END

Dynamic table names in stored procedure function

I've written a stored procedure function to get a name from a table. The trouble is that I want the table name to be passed in as a parameter (there are several different tables I need to use this function with):
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `getName`(tableName VARCHAR(50), myId INT(11)) RETURNS VARCHAR(50)
begin
DECLARE myName VARCHAR(50);
SELECT
'name' INTO myName
FROM
tableName
WHERE
id=myId;
RETURN myName;
end
This method has an error because it uses the variable name "tableName" instead of the actual value of the variable.
I can work around this problem in a procedure by using a CONCAT like this:
SET #GetName = CONCAT("
SELECT
'name'
FROM
",tableName,"
WHERE
id=",myId,";
");
PREPARE stmt FROM #GetName;
EXECUTE stmt;
...but, when I try to do this in a function I get a message saying:
Dynamic SQL is not allowed in stored function or trigger
I tried to use a procedure instead, but I couldn't get it to just return a value, like a function does.
So, can anyone see a way to get around this problem. It seems incredibly basic really.
If you want to buld a SQL statement using identifiers, then you need to use prepared statements; but prepared statements cannot be used in functions. So, you can create a stored procedure with OUT parameter -
CREATE PROCEDURE getName
(IN tableName VARCHAR(50), IN myId INT(11), OUT myName VARCHAR(50))
BEGIN
SET #GetName =
CONCAT('SELECT name INTO #var1 FROM ', tableName, ' WHERE id=', myId);
PREPARE stmt FROM #GetName;
EXECUTE stmt;
SET myName = #var1;
END
Using example -
SET #tableName = 'tbl';
SET #myId = 1005;
SET #name = NULL;
CALL getName(#tableName, #myId, #name);
SELECT #name;