Why won't this Azure SQL Stored Procedure execute? - sql

I can't figure out why this won't execute.
Created the SP with:
CREATE PROCEDURE [dbo].[sp_custom_addADUser]
#userNameParam NVARCHAR(255)
AS
CREATE USER [#userNameParam]
FROM EXTERNAL PROVIDER
WITH DEFAULT_SCHEMA = dbo;
ALTER ROLE testRole ADD MEMBER [#userNameParam]
I have GRANT EXECUTE ON dbo.sp_custom_addADUser permissions:
|role_name| user_name |
|---------|--------------------|
|testRole |myname#mycompany.com|
|name |ObjectType |PermissionType |permission_name|state_desc|ObjectType|SchemaName|ObjectName|
|--------|-------------|----------------|---------------|----------|----------|----------|----------|
|testRole|DATABASE_ROLE|OBJECT_OR_COLUMN|EXECUTE|GRANT|SQL_STORED_PROCEDURE|dbo|sp_custom_addADUser|
From SSMS, execute the SP by passing it the string: someone#mycompany.com (actual user).
Error is:
Msg 15007, Level 16, State 6, Procedure dbo.sp_custom_addADUser, Line 12 [Batch Start Line 0]
'#userNameParam' is not a valid login or you do not have permission.
Msg 15151, Level 16, State 1, Procedure dbo.sp_custom_addADUser, Line 16 [Batch Start Line 0]
Cannot add the principal '#userNameParam', because it does not exist or you do not have permission.
(1 row affected)

The error message gives a hint of what's happening -- it's using the parameter name #userNameParam where you would want it to be using the value someone#mycompany.com.
This is because CREATE USER can't accept a parameter (as this previous answer mentions).
One option is to use dynamic SQL. I.e., build the SQL statement you want to execute as a string -- concatenating in the value of the parameter -- and then run it with sp_executesql.
Make sure you understand the risks of SQL Injection and how to mitigate them before using dynamic SQL.

It's reading [#userNameParam] as a quoted identifier (like a column name), not as a variable, because of the square brackets. However, you can't use a variable for an object name, like a user name for instance, so just removing the brackets won't get the job done. You'll need a touch of dynamic SQL to make it all happen.
With any dynamic SQL, you'll want to include measures to avoid SQL injection, so I used both a parameterized query and QUOTENAME for the example code. The #CRLF variable just injects carriage return/line feeds into the dynamic string for readability when you print the result for debugging. There are multiple lines in the string builder for readability when looking at the procedure code.
See the excellent link in Tim Goodman's answer for more details here.
CREATE PROCEDURE [dbo].[sp_custom_addADUser]
#userNameParam NVARCHAR(255)
AS
DECLARE #sqlCreate NVARCHAR(500)
,#sqlAlter NVARCHAR(500)
,#CRLF CHAR(2) = CHAR(10)+CHAR(13);
SET #sqlCreate = CONCAT(#sqlCreate, 'CREATE USER #userName', #CRLF);
SET #sqlCreate = CONCAT(#sqlCreate, 'FROM EXTERNAL PROVIDER', #CRLF);
SET #sqlCreate = CONCAT(#sqlCreate, 'WITH DEFAULT_SCHEMA = dbo;');
--PRINT #sqlCreate;
EXECUTE sp_executesql #sqlCreate, N'#userName', QUOTENAME(#userNameParam);
SET #sqlAlter = ('ALTER ROLE testRole ADD MEMBER #userName;');
--PRINT #sqlAlter;
EXECUTE sp_executesql #sqlAlter, N'#userName', QUOTENAME(#userNameParam);

Related

How to count rows in SSIS based on specific conditions?

I have a Stored Procedure in SQL Server 2008 like below.
ALTER PROCEDURE myStoredProcedure
#Id int,
#hin varchar(30),
#checkValue varchar(30),
#CounterDeceasedPatients int=0 OUTPUT
insert into myTable
values (#Id, #hin, GETDATE())
if (#checkValue is not null)
BEGIN
set #CounterDeceasedPatients = #CounterDeceasedPatients + 1;
update myTable
set hin= #checkValue
where Id = #Id
RETURN;
END
I am calling this SP via SSIS, by using an OLE DB Command in Data Flow, which enables each rows in my file go to the SP - with the sql command: EXEC [dbo].[myStoredProcedure] ?,?,?. (The order of data (?) in my file is: Id, hin, checkValue)
What I want to do is to count how many different records (different rows) entered the if condition in my SP. SO I believe need to place a "row counter" somewhere, filtering its usage where #checkValue is not null. But I couldnt find it how. I am a newbie in SSIS, so I appreciate if someone helps me to figure this out. Thanks.
EDIT: I am trying to select only #checkValue as an input parameter for my ROW COUNT, but it is giving error:
EDIT2: I updated my SP. I added "CounterDeceasedPatients" variable as Int32 in SSIS and assigned it to 0. My sql execute command is: EXEC [dbo].[myStoredProcedure] ?,?,?,?,CounterDeceasedPatients
This is giving me the error:
Source: "Microsoft OLE DB Provider for SQL Server" Hresult:
0x80040E07 Description: "Error converting data type nvarchar to
int.".
When I use EXEC [dbo].[myStoredProcedure] ?,?,?,?,CounterDeceasedPatients output as SQL command, then I receive the error:
Description: "Cannot use the OUTPUT option when passing a constant to
a stored procedure.
I need help.
Use a script transformation and a DataFlow-level package variable.
Create the int-type variable with a default of 0, and in the script transformation, increment the variable if checkvalue is not null for the incoming row, and then use the value of the variable to set the value of your counter column.
Note that I am suggesting this INSTEAD of trying to update the counter with an OUTPUT variable in your stored procedure, and not as a way of trying to get that idea to work.

Creating a view in tempdb in SQL Server through a post request

Since I need to send the query statement through a http post request there are certain limitations.
1. It should be a one liner
2. it should be created in tempdb since i am going to drop it afterwards.
since SQL server takes CREATE VIEW statement only in new line I am feeding new line characters to the statement. here is the statement:
DECLARE #NewLineChar AS CHAR(2) = CHAR(13) + CHAR(10); ('USE tempdb;' +#NewLineChar + 'GO' +#NewLineChar +'CREATE VIEW temp_view AS select name from sys.databases')
This query gives me following error:
Msg 102, Level 15, State 1.
Incorrect syntax near 'USE tempdb;'. (Line 1)
what could be the problem ?
Thanks
edit: The same query works like this
USE tempdb;
GO
CREATE VIEW temp_view AS select name from sys.databases
where is the syntax error?
Since SQL server takes CREATE VIEW statement only in new line I am feeding new line characters to the statement.
I have never heard of such a requirement. What the documentation does state is: The CREATE VIEW must be the first statement in a query batch.
The statement you have in your question doesn't make sense. You can't just drop a VARCHAR in SSMS and expect SQL Server to just execute it.
What you probably want is something like the following:
USE tempdb;
DECLARE #stmt NVARCHAR(MAX)=N'CREATE VIEW temp_view AS SELECT name FROM sys.databases;';
EXECUTE sp_executesql #stmt;
Or in one line:
USE tempdb;DECLARE #stmt NVARCHAR(MAX)=N'CREATE VIEW temp_view AS SELECT name FROM sys.databases;';EXECUTE sp_executesql #stmt;
This is a bit long for a comment.
You can create a view in the current database with a name like _temp_<something>. You can even include session information if you want to emulate temporary tables.
Or, create a temporary table with no rows:
select top 0 *
into #temp
from <whatever>;
You can access the structure of this table.
If you are using a very recent version of SQL Server, use sp_describe_first_result_set (see here).

Can I use a variable as the value of the option AUDIT_GUID for the CREATE SERVER AUDIT statement?

I am trying to make the Audit_GUID value in the CREATE SERVER AUDIT command dynamic by using the NEWID() function in SQL. Below is my SQL script to do this:
USE [master]
GO
DECLARE #newGUID as uniqueidentifier
SET #newGUID = NEWID()
CREATE SERVER AUDIT Audit_Select_Queries -- Name of the Audit(unique for a Server)
TO FILE
( FILEPATH = N'XXXX' -- Folder to Store Audit Files at
,MAXSIZE = 0 MB -- 0 = UNLIMITED
,MAX_ROLLOVER_FILES = 2147483647 -- Max possible number of Files
,RESERVE_DISK_SPACE = OFF
)
WITH
( QUEUE_DELAY = 1000 -- Delay Audit actions by this time for completion
,ON_FAILURE = CONTINUE -- Database operation is more important than Audit
,AUDIT_GUID = #newGUID -- UUID of the Audit (unique for a server)
)
ALTER SERVER AUDIT Audit_Select_Queries WITH (STATE = OFF)
GO
But I get a syntax error near #newGUID saying "Incorrect syntax near '#newGUID'"
Please let me know what am I doing wrong.
EDIT: I am working on Microsoft SQL Server 2012
No ...
CREATE SERVER AUDIT is a statement – so AUDIT_GUID isn't a 'parameter' in the same way that a SQL Server parameter of a stored procedure is a parameter. If you're familiar with other languages, you could consider CREATE SERVER AUDIT as a 'special form' and, as such, you simply need to remember that it doesn't accept variables for that option.
I can understand why that's confusing as, for example, the BACKUP statement(s) do allow variables for certain 'parameters' ("options"), namely the name of the database; e.g. this is perfectly valid T-SQL:
DECLARE #databaseName nvarchar = "insert_name_of_database_here";
BACKUP DATABASE databaseName
...
For clarifying these types of questions, just consult Microsoft's documentation for the relevant version of SQL Server if you can't remember whether some parameters or options accept variables or not. [You can easily open the relevant documentation from SSMS by highlighting the statement, built-in procedure, etc. and hitting F1 on your keyboard.]
But if You're Willing to Dynamically Generate the T-SQL ...
Here's how you can use dynamic SQL – via EXECUTE or sp_executesql – to do what you're trying to do:
DECLARE #dynamicSql nvarchar(1000);
SELECT #dynamicSql = 'CREATE SERVER AUDIT
...
AUDIT_GUID = ''' + CAST(#newGUID AS nvarchar(255)) + ''''
+ '...' + ...,
EXEC sp_executesql #dynamicSql;

Error in SQL stored procedure

I am getting the following error when I execute my stored procedure:
Msg 102, Level 15, State 1, Line 6Incorrect syntax near '2011'.(1 row(s) affected)
Here is the stored procedure:
ALTER PROCEDURE [dbo].[DeliveryFileNames]
AS
BEGIN
SET NOCOUNT ON;
declare #SQL nvarchar(4000)
Create Table #DelivTemp(
Style nvarchar(50),
Material nvarchar(50),
Filename nvarchar(100),
delivered_date date)
set #SQL=
N'insert into #DelivTemp
Select distinct Style,Material,filename
from OPENQUERY(GCS_PRODUCTION,
''SELECT LEFT(FILENAME,locate(''''_'''',FILENAME)-1)as Style,
substring_index(filename,''''_'''',2)as Material,filename,
delivered_date FROM view_delivery_log
where delivered_date > ''2011%'' order by Style '')'
exec (#SQL)
drop table dbo.DelivFN
Select * into dbo.DelivFN
from #DelivTemp
END
I am using OpenQuery to update a SQL table from a linked server on SQL Server 2008 R2.
I know that the underscore is a real issue, but I have tried a plethora of options including \, % and both single and double quotes.
Regardless I am getting the same result. I can run the query independently of the stored procedure and achieve the correct results. The filename field referenced several times is formatted 00000000_ABC4_A.png. I am using the underscore to identify the components of the file name that I need for my reporting purposes.
In addition to the the logical error of your date comparison using the % that the others have pointed out, your current issue is a syntactical error.
Since you've got a dynamic sql statement contained within another dynamic sql statement... you'll need to double-escape all of your single quotes... which you did in most of the query, except for the following line:
where delivered_date > ''2011%'' order by Style '')'
Properly escaped, would be:
where delivered_date > ''''2011%'''' order by Style '')'
Which raises the question... why are you building up the string to execute dynamically, instead of just calling the statement directly?
It's the syntax of ''2011%''. This is not a valid date. % being a wildcard means the compiler can't know what to compare against in the WHERE clause. You'd need to use an actual date: i.e. ''2011_01_01'' so the compiler can know what to compare against
I believe the stored proc exec runs under a different session, therefore you won't have access to the temp table anyway. So, it won't matter if you get that sql statement to run. You could always use YEAR(delivered_date) > 2011.
Another approach would be to use the fqn for the linked server to select into and bypass the temp table all together:
SELECT LEFT(FILENAME,locate('_',FILENAME)-1)as Style,
substring_index(filename,'_',2)as Material,filename,delivered_date
FROM [linked_server_name].[db_name].[dbo].view_delivery_log
into dbo.DelivFN

How do I paramaterise a T-SQL stored procedure that drops a table?

I'm after a simple stored procedure to drop tables. Here's my first attempt:
CREATE PROC bsp_susf_DeleteTable (#TableName char)
AS
IF EXISTS (SELECT name FROM sysobjects WHERE name = #TableName)
BEGIN
DROP TABLE #TableName
END
When I parse this in MS Query Analyser I get the following error:
Server: Msg 170, Level 15, State 1, Procedure bsp_susf_DeleteTable, Line 6
Line 6: Incorrect syntax near '#TableName'.
Which kind of makes sense because the normal SQL for a single table would be:
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'tbl_XYZ')
BEGIN
DROP TABLE tbl_XYZ
END
Note the first instance of tbl_XYZ (in the WHERE clause) has single quotes around it, while the second instance in the DROP statement does not. If I use a variable (#TableName) then I don't get to make this distinction.
So can a stored procedure be created to do this? Or do I have to copy the IF EXISTS ... everywhere?
You should be able to use dynamic sql:
declare #sql varchar(max)
if exists (select name from sysobjects where name = #TableName)
BEGIN
set #sql = 'drop table ' + #TableName
exec(#sql)
END
Hope this helps.
Update: Yes, you could make #sql smaller, this was just a quick example. Also note other comments about SQL Injection Attacks
Personally I would be very wary of doing this. If you feel you need it for administrative purposes, please make sure the rights to execute this are extremely limited. Further, I would have the proc copy the table name and the date and the user executing it to a logging table. That way at least you will know who dropped the wrong table. You may want other protections as well. For instance you may want to specify certain tables that cannot be dropped ever using this proc.
Further this will not work on all tables in all cases. You cannot drop a table that has a foreign key associated with it.
Under no circumstances would I allow a user or anyone not the database admin to execute this proc. If you havea a system design where users can drop tables, there is most likely something drastically wrong with your design and it should be rethought.
Also, do not use this proc unless you have a really, really good backup schedule in place and experience restoring from backups.
You'll have to use EXEC to execute that query as a string. In other words, when you pass in the table name, define a varchar and assign the query and tablename, then exec the variable you created.
Edit: HOWEVER, I don't recommend that because someone could pass in sql rather than a TableName and cause all kinds of wonderful problems. See Sql injection for more information.
Your best bet is to create a parameterized query on the client side for this. For example, in C# I would do something like:
// EDIT 2: on second thought, ignore this code; it probably won't work
SqlCommand sc = new SqlCommand();
sc.Connection = someConnection;
sc.CommandType = Command.Text;
sc.CommandText = "drop table #tablename";
sc.Parameters.AddWithValue("#tablename", "the_table_name");
sc.ExecuteNonQuery();