SQL Server : check if property exist with true or false - sql-server

I am creating a login page that receives a value (string) of an email address and also has a property of email confirmed (bool). I am trying to create a stored procedure which checks if the email address that is received from the client side matches with a string value in the database. Any suggestions for this type of query?

SELECT COUNT(*) FROM dbo.EmailAddressTable WHERE EmailAddress = #YourClientEmailAddress
This query will return the number of rows in your table with the email address provided by the client. If you have 1 or more rows, you have a match. If you have 0 rows, you don't.

#Email nvarchar(256)
, #EmailConfirmed bit output
as
begin
/*
DECLARE #EmailConfirmed bit
EXEC dbo.UserProfiles_CheckIfConfirmed 'c12#sab.com' , #EmailConfirmed OUTPUT
SELECT #EmailConfirmed
DECLARE #EmailConfirmed bit
EXEC dbo.UserProfiles_CheckIfConfirmed 'john#sab.la' ,#EmailConfirmed OUTPUT
SELECT #EmailConfirmed
*/
SELECT #EmailConfirmed = EmailConfirmed FROM ASPNETUSERS WHERE Email = #Email
END
if 'john#sab.la' exists than EmailConfirmed will output a value of 1. Otherwise 0

Related

SQL Server, CONTEXT_INFO(), and varchar size

In table users I have a column username of datatype varchar(50). The table has no records. I insert a new record with A for the username. The following returns what I would expect:
SELECT username, LEN(username)
FROM users
WHERE id = 1 -- returns: A, 1
So far so good.
Now I update table users from a trigger on another table, using the value from CONTEXT_INFO():
set #context = cast('B' as varbinary(128))
set CONTEXT_INFO #context
update some_other_table
set x = 'y'
where id = 97
In the trigger for some_other_table I do:
DECLARE #context VARCHAR(128)
SELECT
#context = CAST(CONTEXT_INFO() AS VARCHAR(128))
FROM
master.dbo.SYSPROCESSES
WHERE
SPID = ##SPID
DECLARE #user VARCHAR(50) = LEFT(#context, 50)
UPDATE users
SET username = LTRIM(RTRIM(#user))
WHERE id = 1
The username is correctly set to "B", but the following now returns 50:
SELECT
username, LEN(username)
FROM
users
WHERE
id = 1 -- returns: B, 50
The solution, when populating the context, is to do:
set #context = cast('B' + replicate(' ', 126) as varbinary(128))
But why do I need to do this?
When I don't pad the CONTEXT_INFO with spaces what is happening that updating using its value will cause the resulting length to be 50 (even if I ltrim and rtrim the single character value before updating)?
And why must I pad my CONTEXT_INFO to 127 bytes total, not 128? For every character over 127, 1 character is truncated from the value originally set on CONTEXT_INFO
Note: ANSI_PADDING is enabled
In your code:
declare #user varchar(50) = left(#context, 50)
UPDATE users set username = ltrim(rtrim(#user)) WHERE id = 1
you defined #user to be 50 characters. Since the CONTEXT_INFO itself is 128 bytes, the contents of #user will be the letter B padded by 49 null CHAR(0) characters. LTRIM() and RTRIM() will not remove null characters, which are not whitespace, so they have no effect on #user.
If you want to remove the NULL character you can try this (assuming you are using SQL Server 2005 or later):
UPDATE users SET username = REPLACE(#user, char(0), '')
I know this question is old, but there are two ways of retreiving CONTEXT_INFO: either directly with CONTEXT_INFO(), or from sys.dm_exec_sessions. The latter is not padded with '0' characters.
declare #c varbinary(128)
select #c = convert(varbinary(128), 'Test context')
set CONTEXT_INFO #c
go
-- option 1: padded with '0'
declare #value varchar(128)
select #value = convert(varchar(128), CONTEXT_INFO())
select #value context, len(#value) length
go
-- option 2: NOT padded with '0'
declare #value varchar(128)
select #value = convert(varchar(128), context_info)
from sys.dm_exec_sessions
where session_id = ##SPID
select #value context, len(#value) length
go
Query result.
context length
--------------- -----------
Test context 128
(1 row affected)
context length
--------------- -----------
Test context 12
(1 row affected)
Alternatively, as of SQL Server 2016 you can also use SESSION_CONTEXT(), which allows you to specify key-value pairs, instead of a single binary blob.

Can I use output variables to get a value?

I have a stored procedure where I'm checking if username exists and if it does not exists insert into user table then gets userid for the same username. I was trying to use an output variable to get the userid. Am I going about this wrong?
alter Procedure Check_User_Name
(
#username varchar(25),
#password varchar(100),
#role_id integer,
#idn nvarchar output
)
As
Begin
IF NOT EXISTS(SELECT idn=#idn
FROM [user] WHERE username = #username)
BEGIN
INSERT INTO [user] (username,[password],role_id) VALUES
(#username, #password,#role_id)
--select #idn=idn from [user]
Print 'UserName inserted successfully'
End
Else IF EXISTS(SELECT * FROM [user] WHERE username = #username)
Begin
Print 'UserName already exists'
End
END
You had it close -- After replacing your column names to match with what is in my user table, this worked for me. (I think I replaced everything back).
On your Else, you're basically doing a second lookup by username - which is unnecessary in this case.
ALTER Procedure Check_User_Name
(
#username varchar(25),
#password varchar(100),
#role_id integer,
#idn nvarchar(20) output
)
As
Begin
IF NOT EXISTS(SELECT idn
FROM [user] WHERE username = #username)
BEGIN
INSERT INTO [user] (username,[password], role_id) VALUES
(#username, #password)
select #idn=idn from [user] WHERE username = #username
Print 'UserName inserted successfully'
End
Else
Begin
Print 'UserName already exists'
SELECT #idn = idn FROM [user]
WHERE username = #username
End
END
GO
Execute with:
DECLARE #idnOut nVarChar(20)
exec Check_User_Name #username = 'user2127184', #password = 'asdf', #role_id = 0, #idn = #idnOut OUTPUT
SELECT #idnOut
You can use out params, but I always avoid out params in SQL like I do in C# for the same reasons.
If you have a result set with just the UserId:
select #UserId;
then you expand it to include other things later:
select #UserId, #UserGuid;
without much trouble.
Even if you need to return a status (Login Allowed / Locked Out) and details you can just use multiple result sets:
select #Status;
select #UserId; //etc.
If you have out parameters you have to change the Signature of the procedure - which can be a problem if you are supporting multiple clients calling it.
The only change you need to make to the calling code with result sets is to keep finding the new column from the result set.

Null values stored in table via Stored Procedure

Following is my stored procedure which stores data in two tables namely SuccessfulLogins and FailedLogins
ALTER procedure [dbo].[Proc_CheckUser]
#UserID VARCHAR(50),
#Password VARCHAR(50)
AS
SET NOCOUNT ON
DECLARE #ReturnVal VARCHAR(500)
DECLARE #PasswordOld VARCHAR(50)
DECLARE #Type NVARCHAR(50)
DECLARE #IP NVARCHAR(50)
SELECT #PasswordOld = Password,#Type=ClientType,#IP=IPAddress
FROM Clients
WHERE Username = #userid
IF (#PasswordOld IS NULL)
BEGIN
SET #ReturnVal='1|Incorrect Username'
INSERT INTO FailedLogins(Username,Password,ClientType,Reason,IPAddress)
VALUES(#UserID,Hashbytes('SHA1',#Password),#Type,'Invalid Username',#IP)
END
ELSE
BEGIN
IF (#PasswordOld!=Hashbytes('SHA1',#Password))
BEGIN
SET #ReturnVal='1|Incorrect Password'
INSERT INTO FailedLogins(Username,Password,ClientType,Reason,IPAddress)
VALUES(#UserID,Hashbytes('SHA1',#Password),#Type,'Invalid Password',#IP)
END
ELSE
BEGIN
SET #ReturnVal='0|Logged in Successfully' +'|'+ rtrim(cast(#Type as char))
INSERT INTO SuccessfulLogins(Username,Password,ClientType,Reason,IPAddress)
VALUES(#UserID,Hashbytes('SHA1',#Password),#Type,'Valid Login Credentials Provided',#IP)
END
END
SELECT #ReturnVal
The problem here is that whenever I enter an Invalid Username,the stored procedure returns the correct message ie Incorrect Username but it stores NULL values in the fields ClientType and IPAddress in Failed Logins Table
Following is my insert query for Invalid username
IF (#PasswordOld!=Hashbytes('SHA1',#Password))
BEGIN
SET #ReturnVal='1|Incorrect Password'
INSERT INTO FailedLogins(Username,Password,ClientType,Reason,IPAddress)
VALUES(#UserID,Hashbytes('SHA1',#Password),#Type,'Invalid Password',#IP)
END
Can anyone help me to rectify this.How to check condition for username?
Thanks
Your code reads
SELECT #PasswordOld = Password,#Type=ClientType,#IP=IPAddress
FROM Clients
WHERE Username = #userid
Wouldn't this mean that no row will be returned for a Username that does not exist? So, the values for ClientType and IPAddress will not get populated and will remain NULL, which would be the expected functionality.
However, if you want to store some value, or these fields are not nullable, assign a static value to these parameters.
Your query is correct. When there is no match for the Username = #UserId , The #Type , #IP variables will be null. Since there is no record in the table for that UserName. What you can do is that in the declaration you can initiate to some default value,so that it will be inserted to table FailedLogins.
DECLARE #Type NVARCHAR(50)="DefaultType/NoType"
DECLARE #IP NVARCHAR(50)="0.0.0.1"
Something like the above.
If the username is invalid it does not appear in the table Clients so your fields pulled from that table will also be NULL. To negate this you could decide to use default values for ClientType and IPAddress using static values in your declarations, but storing this would just be obsolete data and I would think changing the structure of FailedLogins to not store this would seem more logical.

Setting output parameters in SELECT statement with an IF EXISTS check

I am trying to make an efficient SQL stored procedure for retrieving user data from my database, but I am running into a syntax issue I can't seem to figure out.
Basically, I want to assign my output variable within my SELECT statement. I also want to see if the user actually exists by IF EXISTS. Unfortunately, I can't seem to do both.
Here is my procedure:
CREATE PROCEDURE [dbo].FindUser(#UserID binary(16), #UserExists bit OUTPUT, #Name
nvarchar(MAX) OUTPUT)
AS
SET NOCOUNT ON
IF EXISTS (SELECT #Name = Name FROM Users WHERE UserID = #UserID)
BEGIN
SET #UserExists = 1
END
RETURN
Currently, it gives me an "SQL46010 :: Incorrect syntax near #Name." error.
If I remove IF EXISTS, the statement compiles fine!
Why does the IF EXISTS check cause a syntax error?
set #UserExists = 0;
select #Name = Name,
#UserExists = 1
from Users
where UserID = #UserID;
SET NOCOUNT ON
IF EXISTS (SELECT 1 FROM Users WHERE UserID = #UserID)
BEGIN
SET #UserExists = 1
/* do other stuff here select user name or whatever */
END
If there is a record for #UserID in users table Selecting 1 will return true for exists clause and control will enter the BEGIN..END block.

Get Current Sp parameters and values - (reflection alike)?

Lets say I have this simple SP :
CREATE PROCEDURE mySp
#a int,
#b int
AS
BEGIN
...
...
select ________
END
Now let's say I execute it via :
exec mySp 1,2
Question :
Is there any code which I can place in the ______ so it will show me :
"you sent param named a with value 1"
"you sent param named b with value 2"
This code should be dynamic (pasted in some SP's). Hence I can't do :
select "you sent param named a...with value "+cast (#a...)
select "you sent param named b...with value "+cast (#b...)
(it has to read its context parameters and values at runtime)
Is it possible ?
I don't think it is possible to do it cleanly. One really dirty way of doing is to pass in an additional varchar column which is a comma separated list values of rest of the parameters. Then you can get the names of individual params and match them with the passed in parameter. Here's a sample
CREATE PROCEDURE mySp
#a int,
#b INT,
#c varchar(MAX)
AS
BEGIN
declare #names varchar(MAX) = ''
SELECT #names += name +','
FROM sys.parameters
WHERE object_id = ##PROCID AND name <> '#c'
ORDER BY parameter_id
SELECT 'The passed in values for '+#names + ' are ' +#c
END
go
EXEC mySp 1,1, '1,1'

Resources