Execute a set of stored procedures - sql

I am trying to create a stored procedure to execute all the stored procedures in a schema. This is what I have:
ALTER procedure [ALL_IC].[EXECUTE_ICS]
#sql nvarchar(max) = null,
#fa nvarchar(max) = null
as
begin
set #sql = ('select (select ''EXEC [IC].['' + b.name + ''];''
from sys.procedures b
join sys.schemas s on s.schema_id = b.schema_id
where s.name = ''IC''
for xml path(''''))'
) end
EXECUTE sp_executesql #sql
Running this selects the string to execute all the stored procedures, but it doesn't actually execute them.
EXEC ALL_IC.EXECUTE_ICS
How can I actually execute all the stored procedures by running that line of code?
SQL Server 2012

Aside from the numerous awful things this brings to mind the actual logic is quite simple. You just need to build a dynamic string and execute it.
Here is a very simple way you can do this.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'EXEC ' + quotename(s.name) + '.' + quotename(b.name) + ';'
from sys.procedures b
join sys.schemas s on s.schema_id = b.schema_id
where s.name = 'IC'
select #SQL
--exec sp_executesql #SQL
--EDIT--
Changed slightly so the schema is not hard coded inside the dynamic sql. This way if you want a different schema you just change the schema name and everything else will still work.
--Second Edit--
Changed to use QuoteName instead of hardcoding in the []. This is more flexible and stable.

Just to make sure you are not executing any procedures that expect a parameter, I added a little check against the system catalogue sys.parameters. The rest is pretty much the same what you were trying to do in your question.
Declare #sql NVARCHAR(MAX);
SELECT #sql = STUFF((SELECT ' Exec ' + QUOTENAME(s.name) +'.'+ QUOTENAME(p.name) + '; '
FROM sys.procedures P
INNER JOIN sys.schemas S ON P.schema_id = S.schema_id
WHERE s.name = 'IC'
AND NOT EXISTS (SELECT 1
FROM sys.parameters pm
WHERE p.object_id = pm.object_id)
FOR XML PATH('')),1,2,'');
Exec sp_executesql #sql

Related

Invalid object name when iterating over all tables

I am trying to iterate over all the tables with a given schema name and make a copy in the same db with another given schema.
This is the script I am using:
use DoctorWho
declare #sql_query as nvarchar(max)
select #sql_query = concat('insert into doctor_generated.' , table_name , ' select * from ' , table_name , ';')
FROM INFORMATION_SCHEMA.tables
WHERE table_schema LIKE 'dbo%';
exec (#sql_query);
However this throws an error:
Invalid object name 'doctor_generated.tblEpisodeEnemy
Upon searching this error, I've refreshed the local cache & made sure I am using the correct db.
Is there anything I am missing?
I suspect what you actually want is something like this. Firstly use string aggregation for your dynamic statement; I assume you are on a fully supported version of SQL Server as you don't state you aren't. Next use QUOTENAME to properly quote your objects and avoid injection.
Then you can execute your dynamic statement:
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SELECT #SQL = STRING_AGG(N'SELECT * INTO doctor_generated.' + QUOTENAME(t.name) + N' FROM ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N';',#CRLF)
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE s.[name] = N'dbo';
--PRINT #SQL;
EXEC sys.sp_executesql #SQL;

Iterate thru on database tables and delete some of them based on the name of the table

Hello I would like to loop thru my database tables and delete the ones that I don't need. Also I would like this code to be a stored procedure.
I would like to iterate thru on this select's table_name_to_be_deleted:
SELECT name as table_name_to_be_deleted
FROM sys.tables
WHERE 7=7
and name like 'x_%'
and modify_date< dateadd(day,-10,GETDATE())
And drop every table that I have in the table_name_to_be_deleted column
drop table *variable*
Sorry no minimum viable product as I am not that familiar in T-SQL, but I would much appreciate your help!
You can use Dynamic SQL to do this. Making use of the sys.schemas and sys.tables you could do something like this:
CREATE PROC dbo.DeleteArchives #OlderThan date AS
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = STUFF((SELECT #CRLF +
N'DROP TABLE ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.[name])
FROM sys.schemas s
JOIN sys.tables t ON s.schema_id = t.schema_id
WHERE t.[name] LIKE N'x[_]%'
AND t.modify_date < #OlderThan
FOR XML PATH (N''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
EXEC sys.sp_executesql #SQL;
END;

Securing Dynamic SQL from SQL Injection

I have a dynamic script running on all of the objects in a database and change the schema name for every one from [dbo] to the database name.
The script is working just fine, I would like to know if I can do anything better in order to secure it from SQL Injection?
BEGIN TRANSACTION
/* Change schema to all objects in database (from dbo)*/
DECLARE #SchemaName SYSNAME = db_name();
DECLARE #SQL NVARCHAR(MAX) = N'IF Not Exists (select 1 from sys.schemas where schema_id = SCHEMA_ID(#NewSchemaName))
EXEC(''CREATE SCHEMA ''+#NewSchemaName+'''')' + NCHAR(13) + NCHAR(10);
SELECT #SQL = #SQL + N'EXEC(''ALTER SCHEMA ''+#NewSchemaName+'' TRANSFER [' + SysSchemas.Name + '].[' + DbObjects.Name + ']'');' + NCHAR(13) + NCHAR(10)
FROM sys.Objects DbObjects
INNER JOIN sys.Schemas SysSchemas
ON DbObjects.schema_id = SysSchemas.schema_id
WHERE SysSchemas.Name = 'dbo'
AND (DbObjects.Type IN ('U', 'P', 'V'))
EXECUTE sp_executesql #sql, N'#NewSchemaName sysname', #NewSchemaName = #SchemaName
ROLLBACK
In my quest of securing this one, I used this great article by Thom Andrews:
Dos and Don'ts of Dynamic SQL
this is where I started: github.com/NathanLifshes
The script below should be much more secure.
Note the use of the QUOTENAME function in the beginning of the script.
This would work because if you use the QUOTENAME function "inline" inside an EXEC command, you may get a syntax error. So you need to apply it at an earlier stage.
As luck would have it, you have such an "earlier" stage when you initialize the #SchemaName variable:
BEGIN TRANSACTION
/* Change schema to all objects in database (from dbo)*/
DECLARE #SchemaName SYSNAME = QUOTENAME(db_name());
DECLARE #SQL NVARCHAR(MAX) = N'IF Not Exists (select 1 from sys.schemas where schema_id = SCHEMA_ID(#NewSchemaName))
EXEC(''CREATE SCHEMA ''+#NewSchemaName+'''')' + NCHAR(13) + NCHAR(10);
SELECT #SQL = #SQL + N'EXEC(''ALTER SCHEMA ''+#NewSchemaName+'' TRANSFER ' + QUOTENAME(SysSchemas.Name) + '.' + QUOTENAME(DbObjects.Name) + ''');' + NCHAR(13) + NCHAR(10)
FROM sys.Objects DbObjects
INNER JOIN sys.Schemas SysSchemas
ON DbObjects.schema_id = SysSchemas.schema_id
WHERE SysSchemas.Name = 'dbo'
AND (DbObjects.Type IN ('U', 'P', 'V'))
PRINT #SQL
EXECUTE sp_executesql #sql, N'#NewSchemaName sysname', #NewSchemaName = #SchemaName
ROLLBACK
In this case, I don't see an actual risk for SQL injection, since no value is supplied by a user. The script takes only the database name as an input. The only option to utilize SQL injection is by injecting a command into a database name. This is possible, of course. In order to protect against this option, you should use the QUOTENAME function to properly quote the schema name within your dynamic script.

Pass table and column names as parameters and retrieve value

I am trying to built generic query to pass column name I want to count on and table name I want to select value.
So far this is my code:
ALTER PROCEDURE [dbo].[GenericCountAll]
#TableName VARCHAR(100),
#ColunName VARCHAR(100)
AS
BEGIN
DECLARE #table VARCHAR(30);
DECLARE #Rowcount INT;
SET #table = N'SELECT COUNT(' + #ColunName +') FROM ' + #TableName + '';
EXEC(#table)
SET #Rowcount = ##ROWCOUNT
SELECT #Rowcount
END
Trying to execute like this:
EXEC GenericCountAll 'T_User', 'Id';
but looks like I get two results, first result always returning a value of 1, and the second result returns the real count. Can anyone take a look?
Don't create dynamic sql like that! Imagine if I ran:
EXEC GenericCountAll '*/DROP PROCEDURE dboGenericCountAll;--', '1);/*';
The resulting executed SQL would be:
SELECT COUNT(1);/*) FROM */ DROP PROCEDURE dboGenericCountAll;--
That would, quite simply, DROP your procedure. And that's just a simple example. If i knew I could keep doing malicious things, I might even be able to create a new login or user, and make the a db_owner or sysadmin (depending on the permissions of what ever is being used to run that procedure).
I don't know what the point of the ##ROWCOUNT is either, I doubt that's needed. Thus, to make this SAFE you would need to do something like this:
ALTER procedure [dbo].[GenericCountAll]
#TableName sysname, --Note the datatype change
#ColumnName sysname
AS
BEGIN
DECLARE #SQL nvarchar(MAX);
SELECT N'SELECT COUNT(' + QUOTENAME(c.[name]) + N') AS RowCount' + NCHAR(10) +
N'FROM ' + QUOTENAME(s.[name]) + N'.' + QUOTENAME(t.name) + N';'
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
WHERE t.[name] = #TableName
AND c.[name] = #ColumnName;
/*
If either the column or the table doesn't exist, then #SQL
will have a value of NULL. This is a good thing, as it
is a great way to further avoid injection, if a bogus
table or column name is passed
*/
IF #SQL IS NOT NULL BEGIN;
PRINT #SQL; --Your best debugging friend
EXEC sp_executesql #SQL;
END ELSE BEGIN;
RAISERROR(N'Table does not exist, or the Column does not exist for the Table provided.',11,1);
END;
END

How can I retrieve all the stored procedures and their input and output parameters in all the databases from SQL Server?

Apologies, for a convoluted question - I'm not a dba. Is there a simple script I can run that can list all the stored procedures I have on SQL Server, grouped by database and list them with the input and output parameters that go with the stored procedures.
I'm writing a similar script outside of SQL, to do the same for a language calling the stored procedures, so I find if there are conflicts in a legacy application.
Use the following script:
DECLARE #CurrentRowID INT
,#CurrentDatabase SYSNAME;
DECLARE #DynamicSQL NVARCHAR(MAX);
IF OBJECT_ID('tempdb..##DataSource') IS NOT NULL
BEGIN
DROP TABLE ##DataSource;
END;
CREATE TABLE ##DataSource
(
[database] SYSNAME
,[procedure] SYSNAME
,[parameter] SYSNAME
,[is_output] BIT
);
DECLARE #DataBases TABLE
(
[RowID] INT IDENTITY(1,1)
,[database] SYSNAME
);
INSERT INTO #DataBases ([database])
SELECT [name]
FROM [sys].[databases];
WHILE EXISTS(SELECT 1 FROM #DataBases)
BEGIN
SELECT TOP 1 #CurrentRowID = [RowID]
,#CurrentDatabase = [database]
FROM #DataBases;
SET #DynamicSQL = N'INSERT INTO ##DataSource
SELECT ''' + #CurrentDatabase + ''' AS [database]
,PR.[name]
,P.[name]
,P.[is_output]
FROM [' + #CurrentDatabase + '].[sys].[procedures] PR
INNER JOIN [' + #CurrentDatabase + '].[sys].[parameters] P
ON PR.[object_id] = P.[object_id]'
EXEC sp_executesql #DynamicSQL;
DELETE FROM #DataBases
WHERE [RowID] = #CurrentRowID;
END;
SELECT *
FROM ##DataSource
Of course, you can filter some of the databases, or add more columns from the sys.procedures dmv like system type for example.
Run a loop/cursor on sys.databases and inside the loop, USE and run the following query and keep taking union:
SELECT pr.name [Procedure], par.name Parameter, CASE WHEN is_output = 1 THEN 'Output Parameter' ELSE 'Input Parameter' END [ParameterType]
FROM sys.parameters par
INNER JOIN sys.procedures pr ON pr.object_id = par.object_id
ORDER BY [Procedure], [ParameterType]
This will give you all procedures and their dependent parameters.
declare #sql nvarchar(max) = ''
set #sql = #sql + N'union all
select '''+ quotename(d.name) + N''' as db, s.name as sname, p.name as p.name, r. name as paramname, r.is_output as paramoutput
from '''+ quotename(d.name) + N'''.sys.procedures p
join sys.parameters r on p.object_id = r.object_id
join sys.schemas s on p.schema_id = s.schema_id'
from sys.databases d
-- where d.name like ....
order by d.name
set #sql = stuff#sql, 1, 10, N'')
exec sp_executesql #sql
-- not tested