MySQL - Using IF EXISTS seems to be breaking my statement - mysql

I have a stored routine that is meant to create a new desktop login token for my application, that verifies if the user has to relogin (anyway that's not important).
What is important is that the SQL I'm using doesn't seem to like me.
I only ever use T-SQL because I'm a .NET developer who focuses with SSMS for databases, but this time I stupidly decided to use MySQL with no experience XD
Here's the code:
CREATE PROCEDURE `insertNewToken`(IN `Username` VARCHAR(150), IN `Token` VARCHAR(500))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
IF EXISTS(SELECT *
FROM desktopTokens
WHERE desktopTokens.AssignedUsername = Username) THEN
BEGIN
UPDATE desktopTokens
SET desktopTokens.TokenValue = Token
WHERE desktopTokens.AssignedUsername = Username
END
ELSE
BEGIN
INSERT INTO desktopTokens
VALUES
(Username, Token)
END
I keep getting errors at LINE 2 and LINE 15 but have no idea why!
NOTE: Token and Username are SP variables

Try:
DELIMITER $$
CREATE PROCEDURE `insertNewToken`(
IN `Username` VARCHAR(150),
IN `Token` VARCHAR(500)
)
BEGIN
IF EXISTS(SELECT *
FROM desktopTokens
WHERE desktopTokens.AssignedUsername = Username) THEN
UPDATE desktopTokens
SET desktopTokens.TokenValue = Token
WHERE desktopTokens.AssignedUsername = Username
;
ELSE
INSERT INTO desktopTokens
VALUES
(Username, Token)
;
END IF;
END $$
DELIMITER ;

Related

MySQL Stored Procedure - IF EXISTS ... THEN returning unexpected result

The below is my Stored Procedure(Routine) to check whether or not a user with Username(input) exists in the database.
Inside the database, I already have a user with Username - 'dev'.
However, when I ran the below routine, it returned me with res = 1, which I expected it to be -1.
I called the routine this way. Please correct me too if I am calling it the wrong way. I am really new to MySQL Routines.
CALL usp_GetUserValidation ('dev', #ErrorCode)
Can any MySQL Routine pros here enlighten me on this? Thank you in advance guys :)
DELIMITER $$
CREATE PROCEDURE usp_GetUserValidation(IN `#Username` VARCHAR(255), OUT `#ErrorCode` INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'To validate user login'
BEGIN
IF EXISTS
(SELECT UserID
FROM mt_User
WHERE UserName = #Username)
THEN
SET #ErrorCode = -1;
ELSE
SET #ErrorCode = 1;
END IF;
SELECT #ErrorCode AS res;
END$$
DELIMITER ;
It was simply your naming conventions for the parameters. It is finicky and does not like User Variable # signs in them.
You are just testing I can see, as you are returning both a resultset with the info and the OUT variable.
drop procedure if exists usp_GetUserValidation;
DELIMITER $$
CREATE PROCEDURE usp_GetUserValidation(IN pUsername VARCHAR(255), OUT pErrorCode INT)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'To validate user login'
BEGIN
IF EXISTS
(SELECT UserID
FROM mt_User
WHERE UserName = pUsername)
THEN
SET pErrorCode = -1;
ELSE
SET pErrorCode = 1;
END IF;
SELECT pErrorCode AS res;
END$$
DELIMITER ;
Schema:
-- drop table if exists mt_user;
create table mt_User
( UserID int auto_increment primary key,
UserName varchar(100) not null,
unique key(UserName)
);
insert mt_User(UserName) values ('dev');
select * from mt_User;
Test:
set #var1:=-4;
call usp_GetUserValidation('dev',#var1);
-- returns (-1) ---- Yea, we like that
select #var1;
-- (-1)
set #var1:=-4;
call usp_GetUserValidation('dev222',#var1);
-- returns 1 ---- Yea, we like that
select #var1;
-- 1

MySql - if exists clause in stored procedure

I'm working on login/register module of my program based on a tutorial I found.
Everything worked fine but then I switched from MSSql to MySql database.
The problem is I can't rewrite my InsertUser procedure. This procedure takes 3 parameters (username, password and email) and can return 3 different values
-1 if username is already used
-2 if email is already used
id of new row if registration is successful
I tried to write it like this:
DELIMITER $$
CREATE PROCEDURE InsertUser(
IN username VARCHAR(50),
IN pass VARCHAR(255),
IN email VARCHAR(50))
BEGIN
IF (SELECT Id FROM Users WHERE UserName=username) THEN
BEGIN
SELECT -1;
END;
ELSEIF (SELECT Id FROM Users WHERE Email=email)
THEN
BEGIN
SELECT -2;
END;
ELSE
BEGIN
INSERT INTO Users (UserName, Password, RegDate, Email) VALUES(username, pass, CURDATE(), email);
SELECT LAST_INSERT_ID();
END;
END IF;
END $$
DELIMITER ;
When I try to create this procedure using the above code from Visual Studio I get error (wrong syntax). However there is no error if I do it from phpmyadmin page. But it doesn't work at all (returns nothing) regardless of arguments I provide. I'm using phpmyadmin page and execute procedure option to test it.
Here is original T-Sql code:
CREATE PROCEDURE [dbo].[Insert_User]
#Username NVARCHAR(20),
#Password NVARCHAR(20),
#Email NVARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS(SELECT Id FROM Users WHERE Username = #Username)
BEGIN
SELECT -1 -- Username exists.
END
ELSE IF EXISTS(SELECT Id FROM Users WHERE Email = #Email)
BEGIN
SELECT -2 -- Email exists.
END
ELSE
BEGIN
INSERT INTO [Users]
([Username]
,[Password]
,[Email]
,[RegDate])
VALUES
(#Username
,#Password
,#Email
,GETDATE())
SELECT SCOPE_IDENTITY() -- UserId
END
END
Any ideas?
6.10.1 Using Stored Routines from Connector/Net
...
Unlike the command-line and GUI clients, you are not required to specify a special delimiter when creating stored procedures in Connector/Net.
...
One option in MySQL (command-line):
DELIMITER $$
CREATE PROCEDURE `InsertUser` (
IN `_username` VARCHAR(50),
IN `_pass` VARCHAR(255),
IN `_email` VARCHAR(50)
)
BEGIN
IF (SELECT `Id` FROM `Users` WHERE `UserName` = `_username`) THEN
SELECT -1;
ELSEIF (SELECT `Id` FROM `Users` WHERE `Email` = `_email`) THEN
SELECT -2;
ELSE
INSERT INTO `Users` (`UserName`, `Password`, `RegDate`, `Email`)
VALUES (`_username`, `_pass`, CURDATE(), `_email`);
SELECT LAST_INSERT_ID();
END IF;
END$$
DELIMITER ;
Thank you, but I solved it myself. I don't know why I wanted so badly to use EXISTS function:) I got rid of it and now it works:
BEGIN
IF(SELECT Id FROM Users WHERE UserName=username) IS NOT NULL THEN
SELECT -1;
ELSEIF (SELECT Id FROM Users WHERE Email=email) IS NOT NULL THEN
SELECT -2;
ELSE
INSERT INTO Users(UserName, Password, RegDate, Email) VALUES (username, pass, CURDATE(), email);
SELECT LAST_INSERT_ID();
END IF;
END

What is the workaround for using dynamic SQL in a stored Procedure

The Stored Procedure
DELIMITER $$
CREATE PROCEDURE `lms`.`leads_to_bak` ()
BEGIN
SET #table1 = (SELECT `tabler_name` FROM `sets` WHERE `on_off`=0 LIMIT 1);
SET #table2 = CONCAT(#table1, '_bak');
SET #SQL1 = CONCAT('INSERT INTO ',#table2, '(', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table2), ')', ' SELECT ', (SELECT REPLACE(GROUP_CONCAT(COLUMN_NAME), 'lead_id,', '') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #table1), ' FROM ', #table1);
PREPARE stmt FROM #sql1;
EXECUTE stmt;
END$$
DELIMITER ;
The Trigger
DELIMITER $$
USE `lms`$$
CREATE TRIGGER `lms`.`after_insert_into_leads`
AFTER INSERT ON `sets` FOR EACH ROW
BEGIN
CALL lms.leads_to_bak();
END$$
DELIMITER ;
The problem
I get a Error Code: 1336. Dynamic SQL is not allowed in stored function or trigger error message when making an INSERT which by implication would execute the trigger and the stored procedure. I am assuming the problem is the Dynamic SQL here:
PREPARE stmt FROM #sql1;
EXECUTE stmt;
I've looked around and there is a thread on stackoverflow on the problem, but no answer. Does anyone have any suggestions for a plausible workaround?
There is no good workaround for the absense of Dynamic SQL in MySQL functions, just klunky cludges. Some things still remain downright impossible to cludge, such as using a dynamically-calculated field name or table name in a SQL query. Yes, once in a while there is a need for doing this sort of thing!
And don't try cheat by putting the Dynamic SQL in a stored procedure and wrapping in a function or trigger, as the question poser tried - MySQL is too clever and will give you the usual obscure error message. Believe me, I have been around all the houses.
Coming from an Oracle PL/SQL and MS SQL Server background, I sorely miss the richness that PL/SQL and (to a small extent) T-SQL offers for writing procedural SQL.
Within the procedure definition, you need to store all your IN/OUT variables.
Change:
CREATE PROCEDURE `lms`.`leads_to_bak` ()
to:
CREATE PROCEDURE `lms`.`leads_to_bak` (
IN table1 varchar(32),
IN table2 varchar(32),
)
Then call doing this:
CALL `lms`.`leads_to_bak`('table1', 'table2')
replacing the strings with your own.
The purpose of using stored procedures is to prevent SQL injection using strictly typed data. You don't technically need to prepare it in the stored procedure if you ONLY send strictly typed input variables in the parameter list.
This way, you handle the string operations prior to the stored procedure call. Keep your stored procs skinny!
Here's an example of one of my stored procedures:
DELIMITER ;
DROP PROCEDURE IF EXISTS `save_player`;
DELIMITER //
CREATE PROCEDURE `save_player` (
IN uid int(15) UNSIGNED,
IN email varchar(100),
IN name varchar(100),
IN passwd char(96),
IN state ENUM('active','suspended','deleted'),
IN user_role ENUM('gamemaster','moderator','player'),
IN locale ENUM('en','fr'),
IN lvl tinyint(1),
IN hp bigint(20),
IN reborn tinyint(1),
IN cross_ref varchar(12),
IN email_verified tinyint(1),
OUT new_id int(15) UNSIGNED
)
BEGIN
DECLARE date_deleted timestamp DEFAULT NULL;
IF uid > 0 AND EXISTS (SELECT id FROM user WHERE `id`= uid) THEN
IF state = 'deleted' THEN
SET date_deleted = CURRENT_TIMESTAMP;
END IF ;
UPDATE `user` SET
`email` = email,
`name` = name,
`passwd` = passwd,
`state` = state,
`user_role` = user_role,
`locale` = locale,
`lvl` = lvl,
`hp` = hp,
`reborn` = reborn,
`cross_ref` = cross_ref,
`email_verified` = email_verified,
`date_deleted` = date_deleted
WHERE `id` = uid;
SET new_id = uid;
ELSE
INSERT INTO user (`email`, `name`, `passwd`, `state`, `user_role`, `locale`, `lvl`, `hp`, `reborn`, `cross_ref`, `email_verified`, `date_created`)
VALUES (email, name, passwd, state, user_role, locale, lvl, hp, reborn, cross_ref, email_verified, NOW());
SELECT LAST_INSERT_ID() INTO new_id;
END IF;
END //
DELIMITER ;

MYSQL: Procedure with if statement

I'm trying to make a routine that first checks a users password, if it's correct it shall return some values from a different table or change some values in a row.
Is this even possible without making two queries that you handle in PHP? First call for the password, check if its correct then allow the user to make the name change.
Here an example of getting the Rows in User with email and password.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_user_info`(
IN in_Email VARCHAR(45),
IN in_Pass VARCHAR(45)
)
BEGIN
SELECT * FROM User WHERE Email = in_Email AND Pass = in_Pass;
END
And this is what Ive got so far:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `change_pass`(
in_Email VARCHAR(45),
in_PassOld VARCHAR(45),
in_PassNew VARCHAR(45)
)
BEGIN
SET #PassOld = (SELECT Pass From User WHERE Email = in_Email);
IF(#PassOld = in_PassOld) THEN
UPDATE User SET Pass = in_PassNew WHERE Email = in_Email;
END IF;
ENDND IF;
END
Thanks for all the help!
You should really hash those passwords, use the following code
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `change_pass`(
in_Email VARCHAR(45),
in_PassOld VARCHAR(45),
in_PassNew VARCHAR(45)
)
BEGIN
DECLARE KnowsOldPassword INTEGER;
SELECT count(*) INTO KnowsOldPassword
FROM User
WHERE Email = in_Email AND passhash = SHA2(CONCAT(salt, in_PassOld),512);
IF (KnowsOldPassword > 0) THEN
UPDATE User
SET Passhash = SHA2(CONCAT(salt, inPassNew),512)
WHERE Email = in_Email;
END IF;
END $$
DELIMITER ;
The salt is an extra field in table user that is more or less random, but does not need to be secret. It serves to defeat rainbow tables.
You can set salt to a short string char(10) or randomish data. e.g.
salt = ROUND(RAND(unix_timestamp(now())*9999999999);
You don't need to update the salt, just generate it once and then store it.
For more on this issue see:
Salting my hashes with PHP and MySQL
How should I ethically approach user password storage for later plaintext retrieval?
A comment on your code
IF(#PassOld == in_PassOld) THEN //incorrect
IF(#PassOld = in_PassOld) THEN //correct, SQL <> PHP :-)

mySQL Create Procedure error + general mysql help

Hi
I'm very very new at MySQL and was wondering if anyone could help me out.
I'm trying to build a procedure for my database and am using the following code.
CREATE PROCEDURE `createuser`(username VARCHAR(100), password VARCHAR(100), email VARCHAR(100)) BEGIN
DECLare returnValue int;
IF EXISTS(SELECT 'True' FROM User_Table WHERE(User_Username = username OR User_Email = email))
BEGIN
SET returnValue = 0;
END;
ELSE
BEGIN
SET returnValue = 1;
INSERT into User_Table(User_Username, User_Password,User_Email) VALUES(username, password,email)
END; END;
I'm getting a error on the BEGIN after the if statement. The error is "Bad syntax near "BEGIN SET returnvalue = 0; END ELSE;
Could you please tell me if im using the IF statement correctly in a mysql sence. It seems to work fine on a MSSql server but not on a Mysql.
Would it also be possible to point me in the direction of some good tutorials for this kinda stuff as the main MYSQL website isn't particularity user friendly.
Cheers
Instead of
IF statement
BEGIN
commands;
END
ELSE
BEGIN
commands;
END
use
IF statement THEN
commands;
ELSE
commands;
END IF;
Here is how I would write your procedure (changes may be a mixture of cosmetic changes and actual changes; suggest experimenting to find out which are which)
CREATE PROCEDURE `createuser` ( IN username VARCHAR(100),
IN password VARCHAR(100),
IN email VARCHAR(100)
)
BEGIN
DECLARE returnValue INT;
IF EXISTS (
SELECT 1
FROM User_Table
WHERE User_Username = username OR User_Email = email
) THEN
SET returnValue = 0;
ELSE
SET returnValue = 1;
INSERT INTO User_Table
(User_Username, User_Password, User_Email)
VALUES
(username, password, email);
END IF;
END;
You don't appear to be doing anything with the variable returnValue. You might need to declare this as OUT or INOUT, depending on what you want to do with it. I haven't got experience of using return values for stored procedures.