Reputation: 141
I am making a stored procedure in order to check that whether a user exists or not. The problem is whenever I pass it the correct username, it executes successfully but when I pass wrong username, it go through an infinite loop.
Where am I going wrong? Here is my Stored Procedure:
CREATE PROCEDURE `VerifyUserNPass`(userParam varchar(50), out result int)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tempUser varchar(50) default '';
DECLARE count int default 0;
DECLARE noRows int;
DECLARE userList cursor for select userName from users;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
select count(*) into noRows from users;
OPEN userList;
read_loop: LOOP
FETCH userList into tempUser;
IF tempUser = userParam THEN
SET @count = count + 1;
LEAVE read_loop;
ELSEIF count > noRows THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE userList;
select count into result;
END
Upvotes: 1
Views: 3046
Reputation: 108400
To fix your code, use a conditional test on the done
variable to determine whether to leave the loop. (The done
variable is initialized to FALSE, and then set to TRUE in the CONTINUE HANDLER when there are no more rows.)
Get rid of the query "count(*)
" query, and the noRows
variable; those are not needed. (In a concurrent system, it's possible for that count(*) query to return a value that differs from the number of rows returned by a later query. (Consider that its possible for other sessions to insert or delete rows while your procedure is running.)
Also get rid of the references to the @count
user variable. You have a mixture of references, to both a user variable @coount
and a procedure variable count
. These are independent variables. There's no need for your procedure to use a user variable. Instead, stick with using variables that are declared in your procedure. (Save the user variables for when you have a need.)
-- select count(*) into noRows from users;
read_loop: LOOP
FETCH userList into tempUser;
IF done THEN
LEAVE read_loop;
END IF;
IF tempUser = userParam THEN
SET count = 1;
LEAVE read_loop;
END IF;
END LOOP;
A more efficient way to code this would be to let the database find the row(s) you are interested in, by adding a WHERE clause on your query. (There's no need for you to fetch rows that aren't going to match the condition you are interested in.)
Modify your cursor definition to include a predicate (i.e. a condition in a WHERE clause) to limit the rows returned:
DECLARE userList cursor for select userName from users
WHERE userName = userParam LIMIT 1;
I don't understand the need for a procedure here. A native SQL statement would be much more efficient, e.g.
SELECT 1 AS found FROM users u WHERE u.userName = 'foo' LIMIT 1;
Upvotes: 1
Reputation: 42444
Make sure you increment your loop counter for all branches not only in the succes branch and have result variable to hold your outcome.
DECLARE userFound int default 0;
......
read_loop: LOOP
FETCH userList into tempUser;
SET @count = @count + 1;
IF tempUser = userParam THEN
SET @userFound = 1
LEAVE read_loop;
ELSEIF count > noRows THEN
LEAVE read_loop;
END IF;
.....
select userFound into result;
And I would expect that this would return the same result (but I'm not an MySql expert)
CREATE PROCEDURE `VerifyUserNPass`(userParam varchar(50), out result int)
BEGIN
select COUNT(userName) into result from users where username = @userParam
END
Upvotes: 0