Mysql procedure throw error when cursor fetch 0 rows - mysql

I have a Mysql table as follows:
CREATE TABLE `login_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` varchar(50) NOT NULL,
`device_id` varchar(50) NOT NULL,
`timestamp` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `login_info_uniq01` (`user_id`, `device_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The table is empty.
The extend_login_timestamp_using_concat procedure is as follows:
DROP PROCEDURE IF EXISTS `extend_login_timestamp_using_concat` ;;
CREATE PROCEDURE `extend_login_timestamp_using_concat`()
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE login_info_id BIGINT;
DECLARE cur CURSOR FOR SELECT `id` FROM `login_info` WHERE `device_id` = 'WEB';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET #extended_timestamp = (SELECT UNIX_TIMESTAMP(DATE_FORMAT(NOW() + INTERVAL 365 DAY,'%Y-%m-01 23:59:59')) * 1000);
SET #id_list = NULL;
SET #id_count = 0;
OPEN cur;
REPEAT FETCH cur INTO login_info_id;
SET #id_list = CONCAT_WS (',', #id_list, login_info_id);
SET #id_count = #id_count + 1;
IF (#id_count = 2) THEN
SET #update_query = CONCAT('UPDATE `login_info` SET `timestamp` = ', #extended_timestamp, ' WHERE `id` in (', #id_list, ')');
PREPARE extend_expire_statement FROM #update_query;
EXECUTE extend_expire_statement;
DEALLOCATE PREPARE extend_expire_statement;
SET #id_list = NULL;
SET #id_count = 0;
END IF;
UNTIL done END REPEAT;
CLOSE cur;
IF (#id_list IS NOT NULL) THEN
SET #update_query = CONCAT('UPDATE `login_info` SET `timestamp` = ', #extended_timestamp, ' WHERE `id` in (', #id_list, ')');
PREPARE extend_expire_statement FROM #update_query;
EXECUTE extend_expire_statement;
DEALLOCATE PREPARE extend_expire_statement;
END IF;
END ;;
DELIMITER ;
Since there is no data in the table, so cursor would fetch 0 rows, and when I run the procedure it return error as follows:
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
But when I insert one or more than one rows which matches the cursor criteria it works fine.
How can I prevent the error when cursor doesn't fetch any row?
Any help will be highly appreciated. Thanks in advance.

The problem is simple.
When the table is empty then your FETCH causes NOT FOUND event which fires the CONTINUE handler. The handler sets the variable and returns. Then the rest of the cycle code is executed - and your code prepares incorrect SQL code which causes the error in your EXECUTE. See it in #update_query variable immediately after the SP call falling.
Use not REPEAT but DO cycle. And put IF statement which checks the variable value and leaves the cycle if it is set immediately after FETCH statement.
fiddle (pay attention - I add comment marks in your SQL lines, so you can see what statement produces errorneous SQL).
PS. Do not mix local variables (done, login_info_id) and user-defined variables (#extended_timestamp, #id_list, #id_count) usage. In SP the local variables usage is preferred.

Related

Create user after insert in SQL table

I want to create a new user with the same initial password every time I insert a row with the username in the table.
I tried but it doesn't work:
CREATE TABLE IF NOT EXISTS Professeur (
professeur_id int NOT NULL AUTO_INCREMENT,
prenom varchar(40) COLLATE utf8_bin NOT NULL,
name varchar(30) COLLATE utf8_bin NOT NULL,
titre char DEFAULT NULL,
PRIMARY KEY (professeur_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE trigger trig_prof AFTER INSERT ON Professeur
FOR EACH ROW CREATE OR replace USER NEW.name#localhost identified BY pwd0;
ERROR:
ER_PARSE_ERROR: 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
'USER NEW.name#localhost identified BY pwd0' at line 2
Triggers have several limitations as described in the fine manual:
"Triggers cannot operate on any tables in the mysql, information_schema or performance_schema database."
Since users are stored in mysql schema (either in global_priv or user) this can't work.
Do things a different way...
Write a Stored Procedure that both creates the table and adds the user. See SQL SECURITY DEFINER for how to temporarily give an enduser root permission. But be aware of the security implications.
Then, to add the table and the user, it is one CALL statement.
Problem solved with a procedure as Rick James has suggested and with Prepared Statements https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html
I put the following code in my script and I execute the source command SOURCE initdb_gestiondesnotes.SQL
After that I call the procedure with: CALL create_professeur();
DELIMITER $$
CREATE PROCEDURE CREATE_PROFESSEUR()
BEGIN
DECLARE v_a INT Default 1 ;
DECLARE v_nom VARCHAR(42);
DECLARE v_prenom VARCHAR(42);
DECLARE v_login VARCHAR(84);
DECLARE v_titre VARCHAR(5);
DECLARE v_pwd VARCHAR(5);
SET v_pwd = 'p';
simple_loop: LOOP
SET v_nom = CONCAT("prof", LPAD(CAST(v_a AS CHAR), 3, '0'));
SET v_prenom = CONCAT("prenom", LPAD(CAST(v_a AS CHAR), 3, '0'));
SET v_titre = CASE WHEN RAND() > .5
THEN 'M'
ELSE 'F' END;
INSERT INTO Professeur (prenom, nom, titre) VALUES (v_prenom, v_nom, v_titre);
SET v_login = CONCAT(v_prenom, v_nom);
SET #sql1 = CONCAT('CREATE OR REPLACE USER ', v_login, '#localhost identified BY \'p\' ');
PREPARE stm1 FROM #sql1;
EXECUTE stm1;
SET #sql2 = CONCAT('GRANT role_professeur TO ', v_login, '#localhost');
PREPARE stm2 FROM #sql2;
EXECUTE stm2;
SET #sql3 = CONCAT('SET DEFAULT ROLE role_professeur FOR ', v_login, '#localhost');
PREPARE stm3 FROM #sql3;
EXECUTE stm3;
SET v_a=v_a+1;
IF v_a=51 THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
DEALLOCATE PREPARE stm1;
DEALLOCATE PREPARE stm2;
DEALLOCATE PREPARE stm3;
END $$
DELIMITER ;
Now I hove 50 user with prenom001prof001, prenom002prof002... as login and 'p' as password. Again with prepared statements I gave each user the role of role_professeur.

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.

I can't seem to declare variables inside this procedure

I can't get this code to work, i keep getting syntax errors, and i don't see anything wrong with the declare sentences.
delimiter //
create procedure Plsql1 (Count int)
begin
CREATE TABLE alumnos(
nombre VARCHAR(7) primary key,
edad INT (7),
sexo INT (2));
DECLARE Count int default 10;
DECLARE Number int default 0;
DECLARE done int = 0;
etiq1: loop
if not done then
INSERT INTO alumnos VALUES(CONCAT('Victor',Number), Number*5, Number%2);
SET N=N+1;
if (Number=Count) SET done=1;
else
leave etiq1;
end if;
end loop;
SELECT * FROM alumnos;
end //
delimiter ;
When i try to get this to work, i get this:
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 'DECLARE Count int default 10;
DECLARE Number int default 0;
DECL' at line 10
However, i can't see any syntax errors in there. Some advice for this, please?
You can create the table like that in the stored procedure:
CREATE PROCEDURE procedure1(IN tableName VARCHAR(255))
BEGIN
SET #sql = CONCAT('CREATE TABLE ', tableName, '(column1 INT(11))');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
// Do all other stuff then...
END
That should work.

Mysql Procedure. Update and Select

I have a procedure call that updates a set of data and then returns the data set for my to render. Everything works fine except that, I cant do both operations at once. If I do the update, then the procedure, won´t return any value and vice versa. I have seen some answers suggesting to use temptables but I could not find how to retrieve the dataset. I appreciate any help even if it comes to improving my query.
CREATE DEFINER=`root`#`localhost` PROCEDURE `prueba`(IN `idUsuario` INT)
MODIFIES SQL DATA
BEGIN
DECLARE flag INT DEFAULT FALSE;
DECLARE done INT DEFAULT FALSE;
DECLARE idNotificacion INT DEFAULT 0;
DECLARE cont INT DEFAULT 0;
DECLARE resultset CURSOR FOR SELECT id FROM notificaciones WHERE involvedA_idUsuario=idUsuario AND active=1;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN resultset;
SET #query = "SELECT * FROM notificaciones n WHERE n.id IN('null'";
the_loop: LOOP
FETCH resultset INTO idNotificacion;
IF done THEN
LEAVE the_loop;
END IF;
SET cont = cont + 1;
SET flag = TRUE;
SET #query = CONCAT(#query," , " ,idNotificacion);
UPDATE notificaciones SET active=0 WHERE id=idNotificacion;
END LOOP the_loop;
CLOSE resultset;
IF flag THEN
SET #query = CONCAT(#query, ")");
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
END
Do you really need a cursor? An option like this, maybe it can be useful:
/* Procedure structure for procedure `prueba` */
/*!50003 DROP PROCEDURE IF EXISTS `prueba` */;
DELIMITER $$
CREATE PROCEDURE `prueba`(`idusuario` INT)
BEGIN
DECLARE `ids` LONGTEXT DEFAULT NULL;
SELECT GROUP_CONCAT(`id`) INTO `ids`
FROM `notificaciones`
WHERE `involveda_idusuario` = `idusuario` AND `active` = 1;
IF (`ids` IS NOT NULL) THEN
SET #`stmt` := CONCAT('UPDATE `notificaciones`
SET `active` = 0
WHERE `id` IN (', `ids`, ')');
PREPARE `exec` FROM #`stmt`;
EXECUTE `exec`;
SET #`stmt` := CONCAT('SELECT `id`, `involveda_idusuario`, `active`
FROM `notificaciones` `n`
WHERE `n`.`id` IS NULL OR `n`.`id` IN (', `ids`, ')');
PREPARE `exec` FROM #`stmt`;
EXECUTE `exec`;
DEALLOCATE PREPARE `exec`;
END IF;
END$$
DELIMITER ;
You must be careful with GROUP_CONCAT and the system variable group_concat_max_len.
SQL Fiddle demo

error in my procedure MySQL find value INT in all tables database

I want find a number of UserId from all tables call searchUser(3,'UserId')
error: 0 14:30:14 call searchUser(3,'UserId') 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 'NULL' at line 1
DELIMITER $$
CREATE PROCEDURE `searchUser`( in_search int(11),in_column_name varchar(50) )
READS SQL DATA
BEGIN
DECLARE trunc_cmd VARCHAR(50);
DECLARE searchUserId int (11);
DECLARE db,tbl,clmn CHAR(50);
DECLARE done INT DEFAULT 0;
DECLARE COUNTER INT;
DECLARE table_cur CURSOR FOR
SELECT concat('SELECT COUNT(*) INTO #CNT_VALUE FROM `',table_name,'` WHERE `', in_column_name,'` = "',in_search,'"') ,table_name,column_name FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = 'comments' and column_name=in_column_name ;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
PREPARE trunc_cmd FROM "TRUNCATE TABLE temp_details;";
EXECUTE trunc_cmd ;
OPEN table_cur;
table_loop:LOOP
FETCH table_cur INTO db, tbl, clmn;
SET #searchUserId = searchUserId;
SELECT searchUserId;
PREPARE searchUserId FROM #searchUserId;
EXECUTE searchUserId;
SET COUNTER = #CNT_VALUE;
SELECT COUNTER;
IF COUNTER>0 THEN
INSERT INTO temp_details VALUES(db,tbl,clmn);
END IF;
IF done=1 THEN
LEAVE table_loop;
END IF;
END LOOP;
CLOSE table_cur;
SELECT * FROM temp_details;
END
Problem must be due to:
DECLARE searchUserId int (11);
and:
SET #searchUserId = searchUserId;
SELECT searchUserId;
PREPARE searchUserId FROM #searchUserId;
EXECUTE searchUserId;
I assume that you are thinking searchUserId has a value. But nowhere in the code you assigned a value to it. By default it is a NULL. And hence the statement EXECUTE searchUserId is translate to EXECUTE NULL. This caused the error you specified.
To resolve it, you should first assign a proper value to the searchUserId variable declared.
BTW, why are you using the same variable name searchUserId for a local variable, global variable, and statement alias? It would confuse the readers of the program and hence is not advised to practice.
I guess problem lies in this statement-creation-statement:
SELECT concat('SELECT COUNT(*) INTO #CNT_VALUE FROM `',table_name,'` WHERE `', in_column_name,'` = "',in_search,'"') ,table_name,column_name FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = 'comments' and column_name=in_column_name ;
Please execute the statement standalone and see what you get.