I'm using SQL Server 2005/2008, I have a stored procedure that doesn't use string concatenation for generating an EXEC statement, but it does use a dynamic name for the stored procedure.
I think the #stored_procedure_name and potentially the #object_name parameter are both vulnerable. However all the Dynamic SQL links I read assume that you are concatenating your SQL statement inside a string - so I'm wondering if it might actually be OK.
Note, just for posting this, I've made the code generic by calling the table objects - so it might not necessarily make logical sense.
Here is the code:
CREATE PROCEDURE [dbo].[my_dodgy_sp]
#object_name varchar(50) = 'All'
AS
BEGIN
DECLARE #stored_procedure_name varchar(100);
DECLARE object_cursor CURSOR FOR
SELECT stored_procedure_name
FROM [dbo].[objects]
WHERE [stored_procedure_name] <> ''
AND ([name] = #object_name)
OPEN object_cursor
FETCH NEXT FROM object_cursor
INTO #stored_procedure_name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC #stored_procedure_name #object_id OUTPUT;
FETCH NEXT FROM object_cursor
INTO #stored_procedure_name
END
CLOSE object_cursor;
DEALLOCATE object_cursor;
END
Try this:
CREATE PROCEDURE [dbo].[my_dodgy_sp]
#object_name varchar(50) = 'All'
AS
BEGIN
DECLARE #stored_procedure_name sysname, #oID INT, #sql NVARCHAR(MAX), #object_id int;
DECLARE object_cursor CURSOR FOR
SELECT object_id, name
FROM sys.procedures
WHERE [name] = #object_name
OPEN object_cursor
FETCH NEXT FROM object_cursor
INTO #oID, #stored_procedure_name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = N'EXEC ['+OBJECT_SCHEMA_NAME(#oid)+N'].['+#stored_procedure_name+N'] #Object_id OUTPUT'
EXEC sp_executesql #sql, N'#object_id int OUTPUT', #ObjectId =#object_id OUTPUT
PRINT #object_id -- we need to do smth with it?
FETCH NEXT FROM object_cursor
INTO #oID, #stored_procedure_name
END
CLOSE object_cursor;
DEALLOCATE object_cursor;
END
BUT
instead of using such a complicated way, may be you just call the parametrized procedure?
Related
I have a SQL set of instructions that I want to execute across multiple databases. I currently have the following SQL code:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE('USE ' + #dbName + N';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM '+ #mySourceTable + N';
EXECUTE dbo.someProcedure #content;');
FETCH NEXT FROM dbCursor INTO #dbName;
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
Basically I want to do the following: I have several databases that all have the same [someUserDefinedType] table type (with the same structure) and a procedure named dbo.someProcedure that receives as a parameter a table of said type (the dbo.someProcedure is not the same across databases, it is specific to each). I want to go through the list of provided databases (#dbList) and execute each stored procedure with data from #mySourceTable. I am not sure if the code above is the best approach, it does not work and gives the error:
Must declare the scalar variable "#mySourceTable".
This variable is already declared at the beginning of the script. What am I doing wrong? Is it possible to pass the data from #mySourceTable using a variable and not create another table for it (I really want to avoid that)?
Try something like this:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
WHILE ##FETCH_STATUS = 0
BEGIN
SET #DynamicTSQLStatement = N'
USE ' + #dbName + ';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM #mySourceTable;
EXECUTE dbo.someProcedure #content;';
FETCH NEXT FROM dbCursor INTO #dbName;
EXEC sp_executesql #DynamicTSQLStatement, N'#mySourceTable someUserDefinedType readonly', #mySourceTable = #mySourceTable
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
When you are executing T-SQL statement with sp_executesql you can pass parameters.
All,
Trying to set a cursor on a table value inside a table variable, but it does not work. can anyone comment on how I can fix this?
** the code below is called from another stored procedure which provides the value for the tablename variable **
ALTER PROCEDURE [dbo].[usrSetLTDNormDist]
-- Add the parameters for the stored procedure here
#TableName Sysname,
---...
DECLARE #SQLCommand1 NVARCHAR(MAX) = N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName'
EXECUTE dbo.sp_executesql #sqlCommand1
-- Open Cursor
Open #RecCursor1
Fetch Next From #RecCursor1
Into #Volume, #TransDate
---...
Add PRINT #SQLCommand1 between the DECLARE and EXECUTE statements to review what is actually being executed. Based on your code snippet, you will see
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName
...that is, the value you set in #TableName is not automagically added to the script. Here's the way I write these things:
DECLARE #SQLCommand1 NVARCHAR(MAX)
SET #SQLCommand1 = replace(N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from <#TableName>'
,'<#TableName>', #TableName)
PRINT #SQLCommand1
EXECUTE dbo.sp_executesql #sqlCommand1
I use the < > characters to make the replaced values stand out.
This script demonstrates the general technique:
create table T (ID int not null)
go
insert into T(ID) values (99)
go
declare #TableName sysname
declare #ID int
set #TableName = 'T'
declare #SQL nvarchar(max) = N'declare boris cursor for select ID from ' +
QUOTENAME(#TableName)
exec sp_executesql #SQL
open boris
fetch next from boris into #ID
while ##FETCH_STATUS = 0
begin
print #ID
fetch next from boris into #ID
end
close boris
deallocate boris
Producing this output:
(1 row(s) affected)
99
However, I will offer my usual caution - if you're in a situation where you want to operate against multiple tables in the same way, this is usually a sign of a broken data model. Usually there ought to be a single table with additional columns containing data that serves to differentiate the values.
I have a SQL User-Defined Table Type. It used in many
stored procedures.Now i need to change a column in that table type.
I tried to drop and recreate the User-Defined Table Type.But SQL Server
doesn't Allow that. It shows up following error.
Msg 3732, Level 16, State 1, Line 3
Cannot drop type 'dbo.UserDefinedTableType' because it is being referenced by object 'SP_DoSomething'. There may be other objects that reference this type.
Msg 219, Level 16, State 1, Line 3
The type 'dbo.UserDefinedTableType' already exists, or you do not have permission to create it.
How to alter the User-Defined Table Type without modifying all the Stored procedure that uses User-Defined Table Type ?
You have binding in SP_DoSomething stored procedure. The type you want to change is used in that stored procedure.
You need to save script of that procedure. Drop it. Change dbo.UserDefinedTableType and create procedure again.
There is a similar post here. Check is some of the answers can help you. Answer of #norlando seems promising.
In total you should delete all Functions and Stored Procedures which use this User-Defined Table Type. Then you can drop User-Defined Table Type and recreate it. Then you should recreate all Stored Procedures and Functions which you deleted in previous step.
You can use this command for drop and recreate all SPs and Functions.
I suggest you to run this command with Print line to create Drop(s) and Create(s) command. Then you can put between Drop(s) command and Create(s) command your modification.
Declare #fullObjectName NVarChar(1000) = 'ref.Employee'
Declare #CreateCommand VarChar(Max), #DropCommand VarChar(Max)
Declare #ProcList Table
(
RowId Int,
CreateCommand NVarChar(Max),
DropCommand NVarChar(Max)
)
Insert Into #ProcList
SELECT ROW_NUMBER() OVER (ORDER BY OBJECT_NAME(m.object_id)) RowId,
definition As CreateCommand,
'DROP ' +
CASE OBJECTPROPERTY(referencing_id, 'IsProcedure')
WHEN 1 THEN 'PROC '
ELSE
CASE
WHEN OBJECTPROPERTY(referencing_id, 'IsScalarFunction') = 1 OR OBJECTPROPERTY(referencing_id, 'IsTableFunction') = 1 OR OBJECTPROPERTY(referencing_id, 'IsInlineFunction') = 1 THEN 'FUNCTION '
ELSE ''
END
END
+ SCHEMA_NAME(o.schema_id) + '.' +
+ OBJECT_NAME(m.object_id) As DropCommand
FROM sys.sql_expression_dependencies d
JOIN sys.sql_modules m
ON m.object_id = d.referencing_id
JOIN sys.objects o
ON o.object_id = m.object_id
WHERE referenced_id = TYPE_ID(#fullObjectName)
-----
Declare cur_drop SCROLL Cursor For Select CreateCommand, DropCommand From #ProcList
OPEN cur_drop
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
While ##FETCH_STATUS = 0
Begin
--Exec sp_executesql #DropCommand
PRINT #DropCommand
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
End
/*
Drop And ReCreate User Defined Table Type
*/
Fetch First From cur_drop Into #CreateCommand, #DropCommand
While ##FETCH_STATUS = 0
Begin
--Exec sp_executesql #CreateCommand
PRINT #CreateCommand
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
End
Close cur_drop
Deallocate cur_drop
The code below while incomplete should be a good start. Please note that among many other things:
you must adapt it (I am using user type, not table type) and test it.
It only handles procs.
If your procs definition start with alter, you need to add code and logic to control this and deal with it in the cursor (create empty proc first then alter).
Using it will also remove all granted rights on the procs.
...
Begin Try
Begin Tran
Declare #procs Table(code nvarchar(max), pname sysname, pschema sysname)
Declare #sql nvarchar(max), #code nvarchar(max), #pname sysname, #pschema sysname
Declare cur_drop Cursor For
Select sp.definition, obj.name, schema_name(obj.schema_id) From sys.sql_modules as sp
Inner Join sys.objects as obj on obj.object_id = sp.object_id
Inner Join sys.dm_sql_referencing_entities ('dbo.TestType', 'TYPE') as dep on dep.referencing_id = sp.object_id
Where obj.Type = 'P'
Open cur_drop
Fetch Next From cur_drop Into #code, #pname, #pschema
While ##FETCH_STATUS = 0
Begin
Print 'Drop '+#pname
Insert into #procs(code, pname, pschema) Select #code, #pname, #pschema
Set #sql = 'Drop proc ['+#pschema+'].['+#pname+']'
Exec sp_executesql #sql
Fetch Next From cur_drop Into #code, #pname, #pschema
End
Close cur_drop
Deallocate cur_drop
-- Drop Type
-- Create Type
Declare cur_create Cursor For
Select code, pname, pschema From #procs
Open cur_create
Fetch Next From cur_create Into #code, #pname, #pschema
While ##FETCH_STATUS = 0
Begin
Print 'Create '+#pname
Exec sp_executesql #code
Fetch Next From cur_create Into #code, #pname, #pschema
End
Close cur_create
Deallocate cur_create
Commit
End Try
Begin Catch
rollback;
throw;
End Catch
You could automate the process of temporary deleting the dependencies and then re-creating them, so you shouldn't bother if you have many dependencies. For insrutctions on how to automate this process, see my answer here.
I have created a SQL Server 2005 stored procedure which finds dependent objects on a particular table.
I want to run this stored procedure for different database and for different tables. I have created cursor for this.
When I write USE #dbname, it tries to find the stored procedure in a #dbname and not the current database.
Can anybody please help me with how do I write this command in a cursor?
DECLARE name_cur CURSOR FOR
SELECT db_name, obj_name from Stats_Usage
WHERE last_user_update > '2011-06-01' ORDER BY db_name
DECLARE #tableName NVARCHAR (800)
DECLARE #dbName NVARCHAR(800)
DECLARE #sql NVARCHAR(900)
OPEN name_cur
FETCH name_cur INTO #dbName, #tableName
WHILE ##Fetch_Status = 0
BEGIN
SET #sql = 'USE '+#dbName +' EXEC proc_depend ' + #tableName
EXEC (#sql)
FETCH name_cur INTO #dbName, #tableName
END
CLOSE name_cur
DEALLOCATE name_cur
GO
You can fully qualify your Stored Procedure name.
Assuming the database your SP resides in is called procs (for example), you could amend your query to use the following:
SET #sql = 'USE '+#dbName +' EXEC procs.dbo.proc_depend ' + #tableName
EXEC (#sql)
Refactor your stored proc to check for dependant objects cross database. You'll want to send it a command like this:
exec proc_depend 'MyDatabase.dbo.MyTable';
Try this instead:
SET #sql = ' EXEC proc_depend ''' #dbName + '.dbo.'+ #tableName + ''';
You'll need to dig into & modify proc_depend to ensure that it can take a fully qualified object name like database.schema.table
I want to create a dynamic command using #sqlQuery variable. I've also declared a cursor (example: #myCursor). How can I "SET #myCursor = CURSOR FOR #sqlQuery". The syntax I just noted doesn't work. I am using SQL 2000.
You should take a look at The Curse and Blessings of Dynamic SQL
You can do it using sp_executesql. Just be sure to open the cursor within the dynamic SQL.
CREATE PROCEDURE OpenCursor (#query nvarchar(max), #cur cursor VARYING OUTPUT)
AS
DECLARE #sql nvarchar(max)
SET #sql = N'SET #cur = CURSOR STATIC FOR ' + #query + '; OPEN #cur'
EXEC sp_executesql #sql, N'#cur cursor OUTPUT', #cur OUTPUT
GO
DECLARE #cur cursor
EXEC OpenCursor 'SELECT * FROM sysobjects', #cur OUTPUT
FETCH NEXT FROM #cur