My businesses application supports only reporting with selected data from SQL server.In one business process I have very complicated stored procedure which using others stored procs and it was designed to print out results as log of job done. What I want to catch that print out and select it as varchar(max) so my app can handle that data and display to user.
Here is sample scenario described in TSQL code:
create procedure sp_test_print_out
as
begin
Print 'Test';
print 'Test 1';
end
go
create procedure sp_test_print_out_to_select
as
declare #printOut varchar(max)
set #printOut = exec sp_test_print_out --How I can achieve this ?
select #printOut
end
go
exec sp_test_print_out_to_select
You can try setting the values in output parameter
create procedure sp_test_print_out
#printMessages varchar(max) output
as
begin
set #printMessages='Test'
Print 'Test';
set #printMessages= #printMessages + CHAR(10)
set #printMessages= #printMessages + 'Test 1'
print 'Test 1';
end
go
create procedure sp_test_print_out_to_select
as
begin
declare #printOut varchar(max)
exec sp_test_print_out #printOut output -- can be achieved using output parameter ?
select #printOut
end
go
exec sp_test_print_out_to_select
There is also one rough and probably BAD way to get selected data from print commands inside stored procedure.
Command xp_cmdshell and sqlcmd can do the JOB. Xp_cmdshell is mostly disabled and not allowed to use at most of SQL servers because of security reasons.
Here is code:
CREATE TABLE #temp
(OUTPUT VARCHAR(MAX));
declare #cmd varchar(800);
set #cmd = 'sqlcmd -d RobotTest -Q "exec sp_test_print_out"';
INSERT INTO #TEMP
exec xp_cmdshell #cmd ;
select output from #temp;
drop table #temp;
Related
I have a simple stored procedure with one parameter #Name which I want to replace with another variable.
I am actually looking for SQL injection character and if name contains -- then it should replace it with blank. The stored procedure shown below, it is executing without an error, but not replacing the string for example let is say user searches for EXEC John'''select * FROM TEST2 -- which has SQL injection statement in it
CREATE PROCEDURE GetStudentDetails
#Name nvarchar(300)
AS
BEGIN
SET NOCOUNT ON;
SELECT #Name = REPLACE(#Name ,'--','');
SET #Name = REPLACE(#Name ,'--','');
SELECT *
FROM TABLENAME
WHERE Name LIKE N'%'+ #Name +'%'
END
Updated stored procedure:
CREATE PROCEDURE GetStudentDetails
#Name nvarchar(300)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SafeSearchItem nvarchar(30);
SELECT #SafeSearchItem = REPLACE(#Name ,N'--',N'')
SET #SafeSearchItem = REPLACE(#Name ,N'--',N'')
SELECT *
FROM TABLENAME
WHERE Name LIKE N'%'+ #SafeSearchItem +'%'
END
EXEC
EXEC John'''select * FROM TEST2 --
In the second stored procedure, I am always able to inject SQL - not sure it is my system?
As it stands, we can't answer the question, as, well there isn't a question applicable for information we're been provided. There is no risk of injection in the SP we have, thus, there is not answer on how to avoid it.
Anyway, instead, what i'm going to do is show firstly why that SP isn't subject to injection and then change it so it would be, and how the limited "fix" in it could easily be avoided.
Firstly, let's start with a simple table and data (I strongly suggest running any following scripts in a Sandbox environment!):
USE Sandbox;
GO
CREATE TABLE InjectionReady (ID int IDENTITY(1,1), SomeText varchar(500));
INSERT INTO InjectionReady
VALUES ('Here is some text'),
('Life is like a box a chocolates'),
('Milk Chocolate is my favourite'),
('Cheese is dairy product'),
('Chocolate is a dairy product'),
('Cows say "moo"!'),
('English Cat says "Meow"'),
('Japanese Cat says "Nyaa"');
GO
OK, and now let's create your SP (amended for our object). and then do some tests:
CREATE PROCEDURE NonInjectionSearch #Wildcard nvarchar(100) AS
SELECT #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SET #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N'%'+ #Wildcard +N'%';
GO
EXEC NonInjectionSearch 'Chocolate';
EXEC NonInjectionSearch '''; DROP TABLE InjectionReady;--';
EXEC NonInjectionSearch '''; DROP TABLE InjectionReady; SELECT ''';
No injection. Great! Ok, now for an SP that could suffer injection:
CREATE PROCEDURE InjectionSearch #Wildcard nvarchar(100) AS
SELECT #Wildcard = REPLACE(#Wildcard ,N'--',N'');
SET #Wildcard = REPLACE(#Wildcard ,N'--',N'');
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N''%'+ #Wildcard + N'%'';'; --Yes, intentional non parametrisation
PRINT #SQL;
EXEC (#SQL);
GO
EXEC InjectionSearch 'Chocolate';
GO
EXEC InjectionSearch '''; CREATE TABLE Injection1(ID int);--'; --This'll fail
GO
EXEC InjectionSearch '''; CREATE TABLE Injection2(ID int); SELECT '''; --Oh! This worked!
GO
So, how could you avoid this? Well, Parametrise your dynamic SQL:
CREATE PROCEDURE ParamSearch #Wildcard nvarchar(100) AS
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'
SELECT *
FROM InjectionReady
WHERE SomeText LIKE N''%'' + #pWildCard +''%'';'; --Yes, intentional non parametrisation
PRINT #SQL;
EXEC sp_executesql #SQL, N'#pWildcard nvarchar(500)', #pWildCard = #Wildcard;
GO
EXEC ParamSearch 'Chocolate';
GO
EXEC ParamSearch '''; CREATE TABLE Injection1(ID int);--'; --Won't inject
GO
EXEC ParamSearch '''; CREATE TABLE Injection2(ID int); SELECT '''; --Oh! this didn't inject either
Dynamic objects bring another level to this, however, I'll only cover this if required; as it stands (like I said at the start) the question asked can't happen for the scenario we have.
Clean up:
DROP TABLE Injection2;
DROP PROC ParamSearch;
DROP PROC InjectionSearch;
DROP PROC NonInjectionSearch;
DROP TABLE InjectionReady;
I have two procedures which basically give an Table output. I have the third Procedure which basically calls these two procedures and gives the output in an csv file format.
Can anyone help me in building this the right way. Below is something I am trying to do:
Each of the two Procedures gives out an output with like around 100k rows, I want to capture that and want to give the output here from the Main procedure in a csv file.(Please let me know if you need more info)
Create PROC MAIN
#InputParam int
AS
Begin
Set NOCOUNT ON;
BEGIN TRY
IF #InputParam not in (1,2)
BEGIN
Print 'Error Message'
END
Else
begin
IF #InputParam=1
BEGIN
Exec StoredProc1
Print 'Stored Procedure StoredProc1 ended at '+Convert(Varchar(25),GETDATE(),21);
End
Else
Begin
Print 'StoredProc1 does not exist'
END
IF #InputParam=2
BEGIN
Exec StoredProc2
Print 'Stored Procedure StoredProc2 ended at '+Convert(Varchar(25),GETDATE(),21);
End
Else
BEGIN
Print 'StoredProc2 does not exist'
END
END -- This is END for ELSE loop
END TRY
Begin Catch
Print 'Input Validation Catch Block with # '+ Convert(Varchar(5),ERROR_NUMBER())+' Msg: '+ERROR_MESSAGE();
End Catch
END
What you may want to try is to declare a table vairable in your main stored procedure. This table needs to match the output from the sub procedures exactly. The syntax is:
DECLARE #Temp TABLE(
Filed1 INT,
Field2 VARCHAR(100),
etc
)
Then you can execute your sub stored procedure and insert into the table you defined, like this:
INSERT INTO #Temp
EXEC StoredProc1
And finally, select from the #Temp
SELECT * FROM #Temp
You will obviously need to fit your exact requirements into this, but it should assist I hope
You cannot output to a CSV directly from a Stored Procedure without some form of code, so that really depends what you are developing with (ie what will call the stored procedure). You could perhaps build a SQL Job to output to CSV.
i want to insert columns date, service and service_count from an existing table to a new table. i want to do it using a procedure but it's not working.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROC [dbo].[SP_INSERTCOLOUMNS]
AS
BEGIN
DECLARE #SQLS NVARCHAR(MAX)
SET #SQLS = 'insert into test(date,service,service_count)
select date,service,count(service) from tbl_OBD_CDRS
group by date,service'
PRINT #SQLS
EXEC SP_EXECUTESQL #SQLS
PRINT 'INSERTED SUCCESSFULLY'
END
if i run the insert command independently it works fine. if i run it using this procedure it says Command(s) completed successfully, but no changes are made in the table "test".
I don't see any issue in your script. Why don't you try to check count in Test table. See if no of records increases in your Test table.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROC [dbo].[SP_INSERTCOLOUMNS]
AS
BEGIN
select date,service,count(service) from tbl_OBD_CDRS
group by date,service
DECLARE #SQLS NVARCHAR(MAX)
SET #SQLS = 'insert into test(date,service,service_count)
select date,service,count(service) from tbl_OBD_CDRS
group by date,service'
PRINT #SQLS
EXEC SP_EXECUTESQL #SQLS
PRINT 'INSERTED SUCCESSFULLY'
END
Try to run this on SQL Server and post your result.
EXEC [dbo].[SP_INSERTCOLOUMNS]
ALTER PROC [dbo].[SP_INSERTCOLOUMNS]
AS
BEGIN
-- select date,service,count(service) from tbl_OBD_CDRS
-- group by date,service
insert into test(date,service,service_count)
select date,service,count(service) from tbl_OBD_CDRS Group by date,service
PRINT 'INSERTED SUCCESSFULLY'
END
use this if any error in table / datatype it throw directly
I am working with sql server 2008
I have a database table that has a column containing a stored procedure name.
I want to query the database table which returns a list of the stored procedure names, and execute them.
The stored procedures are similar all having a select statment. The data returned in this select statement I want to insert in to a data base table.
Pseudo code looks like this:
INSERT INTO MyTable
EXECUTE sp_executesql SELECT StoredProcedureName FROM Table
Anyone able to assist me with correct sql for achieveing the above?
sp_executesql accepts a unicode string not a tsql statement. So you would need to execute your procedure(s) like this:
execute sp_executesql 'execute ' + #storedprocedurename
which will execute a single procedure.
You will need to write some iterative process to populate the #storedprocedurename variable from your source table.
This is pretty much same as #Coltech answer just with cursor.
DECLARE #spname VARCHAR(200)
DECLARE #sql VARCHAR(1000)
DECLARE your_cursor CURSOR FOR
SELECT spname
FROM yourTable;
OPEN your_cursor;
FETCH NEXT FROM your_cursor
INTO #spname;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'EXEC ' + #spname
execute sp_executesql #sql
FETCH NEXT FROM your_cursor
INTO #spname;
END
CLOSE your_cursor;
Is there a way to persist a variable across a go?
Declare #bob as varchar(50);
Set #bob = 'SweetDB';
GO
USE #bob --- see note below
GO
INSERT INTO #bob.[dbo].[ProjectVersion] ([DB_Name], [Script]) VALUES (#bob,'1.2')
See this SO question for the 'USE #bob' line.
Use a temporary table:
CREATE TABLE #variables
(
VarName VARCHAR(20) PRIMARY KEY,
Value VARCHAR(255)
)
GO
Insert into #variables Select 'Bob', 'SweetDB'
GO
Select Value From #variables Where VarName = 'Bob'
GO
DROP TABLE #variables
go
The go command is used to split code into separate batches. If that is exactly what you want to do, then you should use it, but it means that the batches are actually separate, and you can't share variables between them.
In your case the solution is simple; you can just remove the go statements, they are not needed in that code.
Side note: You can't use a variable in a use statement, it has to be the name of a database.
I prefer the this answer from this question
Global Variables with GO
Which has the added benefit of being able to do what you originally wanted to do as well.
The caveat is that you need to turn on SQLCMD mode (under Query->SQLCMD) or turn it on by default for all query windows (Tools->Options then Query Results->By Default, open new queries in SQLCMD mode)
Then you can use the following type of code (completely ripped off from that same answer by Oscar E. Fraxedas Tormo)
--Declare the variable
:setvar MYDATABASE master
--Use the variable
USE $(MYDATABASE);
SELECT * FROM [dbo].[refresh_indexes]
GO
--Use again after a GO
SELECT * from $(MYDATABASE).[dbo].[refresh_indexes];
GO
If you are using SQL Server you can setup global variables for entire scripts like:
:setvar sourceDB "lalalallalal"
and use later in script as:
$(sourceDB)
Make sure SQLCMD mode is on in Server Managment Studi, you can do that via top menu Click Query and toggle SQLCMD Mode on.
More on topic can be found here:
MS Documentation
Temp tables are retained over GO statements, so...
SELECT 'value1' as variable1, 'mydatabasename' as DbName INTO #TMP
-- get a variable from the temp table
DECLARE #dbName VARCHAR(10) = (select top 1 #TMP.DbName from #TMP)
EXEC ('USE ' + #dbName)
GO
-- get another variable from the temp table
DECLARE #value1 VARCHAR(10) = (select top 1 #TMP.variable1 from #TMP)
DROP TABLE #TMP
It's not pretty, but it works
Create your own stored procedures which save/load to a temporary table.
MyVariableSave -- Saves variable to temporary table.
MyVariableLoad -- Loads variable from temporary table.
Then you can use this:
print('Test stored procedures for load/save of variables across GO statements:')
declare #MyVariable int = 42
exec dbo.MyVariableSave #Name = 'test', #Value=#MyVariable
print(' - Set #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
print(' - GO statement resets all variables')
GO -- This resets all variables including #MyVariable
declare #MyVariable int
exec dbo.MyVariableLoad 'test', #MyVariable output
print(' - Get #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
Output:
Test stored procedures for load/save of variables across GO statements:
- Set #MyVariable = 42
- GO statement resets all variables
- Get #MyVariable = 42
You can also use these:
exec dbo.MyVariableList -- Lists all variables in the temporary table.
exec dbo.MyVariableDeleteAll -- Deletes all variables in the temporary table.
Output of exec dbo.MyVariableList:
Name Value
test 42
It turns out that being able to list all of the variables in a table is actually quite useful. So even if you do not load a variable later, its great for debugging purposes to see everything in one place.
This uses a temporary table with a ## prefix, so it's just enough to survive a GO statement. It is intended to be used within a single script.
And the stored procedures:
-- Stored procedure to save a variable to a temp table.
CREATE OR ALTER PROCEDURE MyVariableSave
#Name varchar(255),
#Value varchar(MAX)
WITH EXECUTE AS CALLER
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
UPDATE ##VariableLoadSave SET Value=#Value WHERE Name=#Name
IF ##ROWCOUNT = 0
INSERT INTO ##VariableLoadSave SELECT #Name, #Value
END
GO
-- Stored procedure to load a variable from a temp table.
CREATE OR ALTER PROCEDURE MyVariableLoad
#Name varchar(255),
#Value varchar(MAX) OUT
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
IF NOT EXISTS(SELECT TOP 1 * FROM ##VariableLoadSave WHERE Name=#Name)
BEGIN
declare #ErrorMessage1 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage1, 20, -1) with log
END
SELECT #Value=CAST(Value AS varchar(MAX)) FROM ##VariableLoadSave
WHERE Name=#Name
END
ELSE
BEGIN
declare #ErrorMessage2 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage2, 20, -1) with log
END
END
GO
-- Stored procedure to list all saved variables.
CREATE OR ALTER PROCEDURE MyVariableList
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
SELECT * FROM ##VariableLoadSave
ORDER BY Name
END
END
GO
-- Stored procedure to delete all saved variables.
CREATE OR ALTER PROCEDURE MyVariableDeleteAll
WITH EXECUTE AS CALLER
AS
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
If you just need a binary yes/no (like if a column exists) then you can use SET NOEXEC ON to disable execution of statements. SET NOEXEC ON works across GO (across batches). But remember to turn EXEC back on with SET NOEXEC OFF at the end of the script.
IF COL_LENGTH('StuffTable', 'EnableGA') IS NOT NULL
SET NOEXEC ON -- script will not do anything when column already exists
ALTER TABLE dbo.StuffTable ADD EnableGA BIT NOT NULL CONSTRAINT DF_StuffTable_EnableGA DEFAULT(0)
ALTER TABLE dbo.StuffTable SET (LOCK_ESCALATION = TABLE)
GO
UPDATE dbo.StuffTable SET EnableGA = 1 WHERE StuffUrl IS NOT NULL
GO
SET NOEXEC OFF
This compiles statements but does not execute them. So you'll still get "compile errors" if you reference schema that doesn't exist. So it works to "turn off" the script 2nd run (what I'm doing), but does not work to turn off parts of the script on 1st run, because you'll still get compile errors if referencing columns or tables that don't exist yet.
You can make use of NOEXEC follow he steps below:
Create table
#temp_procedure_version(procedure_version varchar(5),pointer varchar(20))
insert procedure versions and pointer to the version into a temp table #temp_procedure_version
--example procedure_version pointer
insert into temp_procedure_version values(1.0,'first version')
insert into temp_procedure_version values(2.0,'final version')
then retrieve the procedure version, you can use where condition as in the following statement
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='first version'
IF (#ProcedureVersion='1.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
--insert procedure version 1.0 here
Create procedure version 1.0 as.....
SET NOEXEC OFF -- execution is ON
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='final version'
IF (#ProcedureVersion='2.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
Create procedure version 2.0 as.....
SET NOEXEC OFF -- execution is ON
--drop the temp table
Drop table #temp_procedure_version