Consider this stored procedure. The actual content of procedure doesn't matter, I am using it only for the sake of the example:
CREATE PROCEDURE [dbo].[temp]
#value1 varchar(50),
#value2 varchar(50),
#value3 varchar(50)
as
begin
Select *
From valuesTable (nolock)
inner join valuesTable2 RL (nolock)
on (ValuesTable.ID = RL.RuleId and RL.Type = 'Something')
where #value1 = ValuesTable.RuleVal02
and cast(#value2 as float) > cast(ValuesTable.RuleVal03 as float) and cast(#value2 as float) < cast(ValuesTable.RuleVal04 as float)
--and (#value3 = ValuesTable.RuleVal05)
and (#value3 = ValuesTable.RuleVal05 or ValuesTable.RuleVal05 = -1)
end
Now imagine that this (not very complex function) isn't working. I already know how to debug it both from Visual Studio and from SQL Server Management Studio, however, both cases are lacking:
The main failing point of this function is the big query that it executes. What I'd like to do then, is to take this query, copy it to a new query window and start executing it and debug it by modifying its various parts.
The basic way to debug that query would be copy it to a new query window, get the parameters from the executing code and then manually replace all the #variables with their actual value. That works, but it seems like a very unnecessary work to me and ideally I'd like to get the query, as it is executed on the server - With literal values instead of the parameters, e.g:
where 'actualValue' = ValuesTable.RuleVal02
and cast(4.2 as float) > cast(ValuesTable.RuleVal03 as float) and cast(4.2 as float) < cast(ValuesTable.RuleVal04 as float)...
Since it sounded like something I can only achieve from a profiler, I launched it. Then I configured the events to capture SP:StmtStarting event, to see the statements executed from stored procedures. To my surprise, however, the statements that I see in the profiler, still show with their parameter and not the actual literal value.
Is a way I could easily copy the actual query that is executed in a stored procedure with the parameters replaced to the literal value of the parameter? Is there a flag in the profiler to do so? Or is there another way?
Try this simple workflow (requires 5 seconds):
Run SSMS.
Find your stored procedure in Object Explorer window.
Right click on it to display context menu and select 'Execute Stored Procedure...' from it.
Set ALL parameters values in the form displayed.
SSMS generates the script for you in new query tab (see script at the end of this answer).
Save this script for further use.
Run Debug -> Start Debugging this script and then Step Into EXEC statement.
Change parameter values and run next debug.
DECLARE #return_value int
EXEC #return_value = [dbo].[temp]
#value1 = N'1',
#value2 = N'2',
#value3 = N'3'
SELECT 'Return Value' = #return_value
GO
you could write your query in a variable, with placeholders instead of actual values, replace the placeholders at runtime and execute it with sp_executesql
obviously it's just a thing you should do for debug purposes and not in production ;)
here's an example: http://sqlfiddle.com/#!3/c8c43/5
there you have your query filled with actual content, inside the variable
Related
I am working on a mutation test framework for SQL Server, for this I need to be able to calculate what lines of a stored procedure, function or trigger are executed when I execute a certain stored procedure.
The difficult part is that I want to know the exact lines or statements being executed from the stored procedure I call.
With a query like this I can see what stored procedures/triggers/functions are being executed, since I know when I call the stored procedure I can use the time to see if it was executed.
SELECT d.object_id, d.database_id,
OBJECT_NAME(object_id, database_id) AS proc_name,
MAX( d.last_execution_time) as last_execution_time,
OBJECT_DEFINITION(object_id) as definition
FROM sys.dm_exec_procedure_stats AS d
WHERE d.database_id = DB_ID()
GROUP BY d.object_id, d.database_id,
OBJECT_NAME(object_id, database_id)
How would I find the lines/statements that have been executed, I also have to know inside what stored procedure/trigger/function the lines/statements exists and in which shema this is. I have to take into account that a IF/ELSE statement may be used.
With this data I can do 2 important things:
generate a code coverage report
optimize what lines to mutate, since I dont have to mutate uncovered lines.
A possible, but not a very nice, solution would be to automaticly change stored procedures to add a line that inserts the previous line into a table, but this will require splitting up the procedure into statements, which I don't know how to do.
Please note that I cannot change the code users want to test with my framework. I can search for patterns and replace but manually changing procedures is NOT a option.
EDIT:
Lets redifine this question: How to split a stored procedure definition into its different statements in a way that does not depend on code style?
and How to add a new statement in between found statements?
EDIT: in the SO post SQL Server: How to parse code into its different statements I have found a way to trace statement execution, but I can't filter it yet.
So the extended events are the solution, this is how I have done it:
IF EXISTS(SELECT * FROM sys.server_event_sessions WHERE name='testMSSQLTrace')
DROP EVENT SESSION testMSSQLTrace ON SERVER;
DECLARE #cmd VARCHAR(MAX) = '';
SELECT #cmd = 'CREATE EVENT SESSION testMSSQLTrace
ON SERVER
ADD EVENT sqlserver.sp_statement_completed
(WHERE (sqlserver.database_name = N''' + DB_NAME() + '''))
ADD TARGET package0.ring_buffer
WITH (
MAX_MEMORY = 2048 KB,
EVENT_RETENTION_MODE = NO_EVENT_LOSS,
MAX_DISPATCH_LATENCY = 3 SECONDS,
MAX_EVENT_SIZE = 0 KB,
MEMORY_PARTITION_MODE = NONE,
TRACK_CAUSALITY = OFF,
STARTUP_STATE = OFF
);'
EXEC (#cmd)
This creates an event that can be fired after every statement completion, this is done dynamicly to filter on the database
Then I have 3 procedures that make controlling this event easy
/*******************************************************************************************
Starts the statement trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StartTrace
AS
BEGIN
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = START;
END
GO
/*******************************************************************************************
Ends the statement trace, this also clears the trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_StopTrace
AS
BEGIN
ALTER EVENT SESSION testMSSQLTrace
ON SERVER
STATE = STOP;
END
GO
/*******************************************************************************************
Saves the statements trace
*******************************************************************************************/
CREATE OR ALTER PROC testMSSQL.Private_SaveTrace
AS
BEGIN
DECLARE #xml XML;
SELECT #xml = CAST(xet.target_data AS xml)
FROM sys.dm_xe_session_targets AS xet INNER JOIN sys.dm_xe_sessions AS xe ON (xe.address = xet.event_session_address)
WHERE xe.name = 'testMSSQLTrace'
INSERT INTO testMSSQL.StatementInvocations (testProcedure, procedureName, lineNumber, statement)
SELECT testMSSQL.GetCurrentTest(),
OBJECT_NAME(T.c.value('(data[#name="object_id"]/value)[1]', 'int')),
T.c.value('(data[#name="line_number"]/value)[1]', 'int'),
T.c.value('(data[#name="statement"]/value)[1]', 'VARCHAR(900)')
FROM #xml.nodes('RingBufferTarget/event') T(c)
WHERE T.c.value('(data[#name="nest_level"]/value)[1]', 'int') > 3
END
GO
These procedures respectivly start and stop the trace and the last one stores the result in a table where it filters on the nest level so my own code is not traced.
Finally I use it a bit like this:
start trace
start tran/savepoint
run SetUp (users code)
run test (users code)
save trace
save trace to variable
rollback tran (also catch errors and stuff like that)
save variable back to table so the trace is not rolled back
Special thanks to #Jeroen Mosterd for originally coming up with a proposal for this solution in this SQL Server: How to parse code into its different statements SO post
You can either:
Add a #DEBUG parameter to each stored procedure you call, or
Log everything you want, or
Only log when you want.
With the #Debug parameter, you can default it to OFF, then call it with ON when you want to trace your statements, with the following code:
IF (#Debug = 1) PRINT 'your tracing information goes here';
If you want to log everything, create a log table and insert a row into it wherever you need to know which statement was executed, such as:
DECLARE #log AS TABLE (msg VARCHAR(MAX));
and
INSERT INTO #log VALUES('your tracing information goes here');
Or you can combine them:
IF (#Debug = 1) INSERT INTO #log VALUES('your tracing information goes here');
Of course these will affect performance even when you don't output/log.
need a bit of help with this sql injection issue:
The following is a version of a parameterised stored procedure. Excluding how it is called from an application, is there anyway to prevent #v_string from being treated as dynamic SQL?
I think this is fairly water tight - there's no execute or concatenated sql, but still inserting a semicolon allows additional data to be returned.
I know there are multiple levels to consider this question on, but I want to know if there is some simple solution I am missing here as the majority of injection fixes involve dynamic queries.
create table dbo.Employee (EmpID int,EmpName varchar(60))
declare
#v_id int,
#v_string varchar(60)
begin
set #v_string='test'''; waitfor delay '0:0:5' --
if #v_id is null
begin
set #v_id = (select EmpID
from Abc.Employee
where EmpName=#v_string);
end
print #v_id
end
is there anyway to prevent #v_string from being treated as dynamic
SQL?
I would not expect #v_string to be treated as dynamic SQL here since the T-SQL code has no EXECUTE or EXECUTE sp_executeSQL. The value will not be executed, but treated as a WHERE clause value not vulnerable to SQL injection.
If this doesn't answer your question, post a full example that demonstrates the value being treated as dynamic SQL.
You're being confused by your own testing. The line:
set #v_string='test'''; waitfor delay '0:0:5' --
Is creating a string #v_string with the value test', and then executing waitfor delay '0:0:5'. Then your actual Employee query is being run.
So if you run your query as is, with your additional example:
set #v_string='test'''; select * from sys.databases
...what will happen is that line of code will set #v_string to be test', then immediately execute select * from sys.databases. Then the rest of your code will run, executing your actual select. So you'll see the result of select * from sys.databases, followed by the result of your Employee query, but only because you actually hard-coded the statement select * from sys.databases into your procedure without realising it :)
If you want the string #v_string to be set to test'; waitfor delay '0:0:5' then you've got the string quoting wrong. It should be:
set #v_string='test''; waitfor delay ''0:0:5'''
I have a procedure which takes as argument the function name and then finds out the full body of that function and stores it in a local variable V_FullString.
I need this output not to show in screen rather to put it in text file.
Is it possible ?
ALTER PROCEDURE [dbo].[SP_DictionaryFunction]
#P_FunctionName VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #V_ObjectName varchar(100) = #P_FunctionName;
DECLARE #V_FULLSTRING VARCHAR(5000) = ''
IF EXISTS (SELECT object_id FROM SYS.objects
WHERE object_id = OBJECT_ID(#V_ObjectName))
BEGIN
DECLARE #V_TABLE TABLE (Line VARCHAR(500))
INSERT INTO #V_TABLE
EXECUTE SP_HELPTEXT #V_ObjectName
SELECT #V_FULLSTRING = #V_FULLSTRING + Line
FROM #V_TABLE
PRINT #V_FULLSTRING
END
END
Writing a file from the server would be a very bad idea. Ideally you'd be writing it at the client. So... how are you executing the SQL? If this is SSMS, just press ctrl+shift+f. If you are using sqlcmd, then the -o {filename} switch should work. If you're executing it via ADO.NET, then File.WriteAllText. Otherwise, you'll have to tell us what client tool you are using.
Note: I expect that the main problem you're seeing is that print is often ignored. You might prefer select, which all tools will respect.
I always prefer to use stored procedures for most SQL commands during development.
One example for select statement.I use this Store porcedure
ALTER proc [dbo].[sp_select] (#tbl varchar(200),#col varchar(max),#cond varchar(max))
as
declare #query varchar(max)
if(#cond!=NULL)
begin
set #query='select '+#col+' from '+#tbl+' where '+#cond
end
else
begin
set #query='select '+#col+' from '+#tbl
end
exec(#query)
GO
I am little conscious SQL Injection atacks. This way is safe from such attack or not??
Any suggestion would be appreciated...
Your stored procedure is completely pointless and only makes it harder to write safe code.
SQL injection is not magic; it's simply input strings with quotes.
Stored procedures do not magically defend against it; they simply encourage you to pass user input as parameters (which you aren't doing).
The correct way to protect against SQL (and other forms of) injection is to change your application code to never concatenate arbitrary text (especially user input) into a structured langauge (such as SQL, HTML, or JSON).
Instead, use parameters, a JSON serializer, or a proper HTML escaper, as appropriate.
No. It very vulnerable to SQL Injection. For example, suppose someone does
exec dbo.sp_select '#Dummy', '(Select Null) As x; Update Employee Set Salary = 1000000 Where EmployeeName = ''me''; Declare #Dummy Table (i int); Select Null ', null
then the query that you build and execute is
select (Select Null) As x; Update Employee Set Salary = 1000000 Where EmployeeName = 'me'; Declare #Dummy Table (i int); Select Null from #Dummy
and your stored procedure which is supposed to only do selects has just updated my salary to 1,000,000.
i need to execute several SQL statements inside an stored procedure, save the result into a variable for later use.
SET NOCOUNT OFF
DECLARE #P TABLE
#P = SELECT d.Periodo
from [SISACT].DEPRECIACIONES d, [SISACT].CONTROL_PERIODO c
where c.vigente = 1 AND d.Periodo = c.periodo
IF ##ROWCOUNT > 0
PRINT 'Periodo ya depreciado'
So later i can do something like this
UPDATE [SISACT].ACTIVOS_FIJOS set ultimo_periodo = #p.periodo ...
I know the #P = SELECT is wrong, i saw an example where they used another stored procedure instead of a SELECT statement, i want to know if this is possible without using a stored procedure. If so, how? I'm just going to use the query once in only one stored procedure so i see no point into putting it in another stored procedure. I'm new to T-SQL and SQL server , i'm learning on my own as well and i'm sorry if this is an stupid question.
P.S: The query in this case should return just one record. I'm using SQL SERVER 2008 Express.
Thank you for any help in advance.
I think that what you want.
SET NOCOUNT OFF
DECLARE #P as CHAR(6)
SELECT p# = d.Periodo from [SISACT].DEPRECIACIONES d, [SISACT].CONTROL_PERIODO c where c.vigente = 1 AND d.Periodo = c.periodo
IF ##ROWCOUNT > 0
PRINT 'Periodo ya depreciado'
and later you can use
UPDATE [SISACT].ACTIVOS_FIJOS set ultimo_periodo = #p