FakeTable with tSQLt remove triggers - triggers

I have jsut started to use tSQLt and is about to test a trigger. I call the FakeTable procedure and do my test but the trigger is not executed. If don't use FakeTable the trigger is executed. That seems to be really bad and I canät find any info that there is any method to readded them.
Then I thought the triggers are removed by FakeTable but I can recreate them after the call and did the following code in my test:
DECLARE #createTrigger NVARCHAR(MAX);
SELECT #createTrigger = OBJECT_DEFINITION(OBJECT_ID('MoveDataFromAToB'))
EXEC tSQLt.FakeTable 'dbo.A';
EXEC(#createTrigger);
I got the following error: "There is already an object named 'MoveDataFromAToB' in the database.{MoveDataFromAToB,14} (There was also a ROLLBACK ERROR --> The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.{Private_RunTest,60})"
Anyone that have any experience with tSQLt and know anyworkaround for this problem?

There is an ApplyTrigger method on the tSQLt backlog, but it is not finished yet. For now you should be able to use this code in your test:
DECLARE #createTrigger NVARCHAR(MAX);
SELECT #createTrigger = OBJECT_DEFINITION(OBJECT_ID('MoveDataFromAToB'));
DROP TRIGGER MoveDataFromAToB;
EXEC tSQLt.FakeTable 'dbo.A';
EXEC(#createTrigger);
You need to drop the existing trigger as FakeTable does not drop the original table. It just renames it, which leaves the old trigger intact; hence the name collision you did see.
The rollback that tSQLt executes at the end of every test will put the dropped trigger back in place (unless you are doing something really bad in your code). If you are worried about that use sp_rename instead of drop on the trigger.
I would put all this into a helper stored procedure within the test class and call it from the tests that need it. That way, once we have a better solution implemented in tSQLt you will have to change only one place in your code.

Thanks Sebastian for your answer. It really helped me too :)
I made a stored proc for the code you gave. I will use this until the 'ApplyTrigger' function is available:
CREATE PROCEDURE [tSQLt].[FakeTableWithTrigger]
#TableName NVARCHAR(MAX),
#TriggerName NVARCHAR(MAX),
#SchemaName NVARCHAR(MAX) = NULL, --parameter preserved for backward compatibility. Do not use. Will be removed soon.
#Identity BIT = NULL,
#ComputedColumns BIT = NULL,
#Defaults BIT = NULL
AS
BEGIN
DECLARE #createTrigger NVARCHAR(MAX);
SELECT #createTrigger = OBJECT_DEFINITION(OBJECT_ID(#TriggerName));
EXEC('DROP TRIGGER ' + #TriggerName);
EXEC tSQLt.FakeTable #TableName, #SchemaName, #Identity, #ComputedColumns, #Defaults;
EXEC(#createTrigger);
END
An example of it's useage in a test is:
exec tSQLt.FakeTableWithTrigger 'dbo.MyTable', 'MyTable_SyncTrigger', #Identity = 1

Related

I can create a stored procure with invalid user defined function names in it

I just noticed that I could alter my stored procedure code with a misspelled user defined function in it.
I noticed that at 1st time I execute the SP.
Is there any way to get a compile error when an SP include an invalid user-defined function name in it?
At compile time? No.
You can, however, use some of SQL's dependency objects (if using MS SQL) to find problems just after deployment, or as part of your beta testing. Aaron Bertran has a pretty nice article rounding up the options, depending upon the version of SQL Server.
Here is an example using SQL Server 2008 sys object called sql_expression_dependencies
CREATE FUNCTION dbo.scalarTest
(
#input1 INT,
#input2 INT
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar int
-- Add the T-SQL statements to compute the return value here
SELECT #ResultVar = #input1 * #input2
-- Return the result of the function
RETURN #ResultVar
END
GO
--Fn Works!
SELECT dbo.ScalarTest(2,2)
GO
CREATE PROCEDURE dbo.procTest
AS
BEGIN
SELECT TOP 1 dbo.scalarTest(3, 3) as procResult
FROM sys.objects
END
GO
--Sproc Works!
EXEC dbo.procTest
GO
--Remove a dependency needed by our sproc
DROP FUNCTION dbo.scalarTest
GO
--Does anything have a broken dependency? YES
SELECT OBJECT_NAME(referencing_id) AS referencing_entity_name,
referenced_entity_name, *
FROM sys.sql_expression_dependencies
WHERE referenced_id IS NULL --dependency is missing
GO
--Does it work? No
EXEC dbo.procTest
GO

Trigger and missing option

I'm developing a trigger that has to check the correctness of a date. I'm have big time trouble creating one, becase no matter how I try to simplify my code, Oracle returns me the same error: ORA-00922: missing or invalid option. I have checked the code over and over again and I really can't figure out where my issue is!
The following is an over-simplified version of the trigger, which gets the same error too.
CREATE OR REPLACE checkDateValidity
BEFORE INSERT
ON Event
FOR EACH ROW
BEGIN
IF :new.month>12
THEN
RAISE_APPLICATION_ERROR(-20101, 'Error: wrong month');
END IF;
END;
I really hope you can help me because at this point I really have no idea of what I am doing wrong.
You are missing the TRIGGER keyword:
CREATE OR REPLACE TRIGGER checkDateValidity
.... ^^^^^^^

Is there anyway to reference the collection of parameters passed to a stored procedure, without referencing each one by name?

Is there any way to reference the collection of parameters passed to a stored proc, without referencing each one by name?
The context here is error logging. I'd like to develop a generic CATCH clause, or sub-clause, that grabs all parameter values as well as other error and execution info and logs it to a table (one or more).
The basic version looks something like this:
CREATE PROC dbo.Proc1 #Param1 INT, #Param2 INT
AS
BEGIN TRY
SELECT 1/0
END TRY
BEGIN CATCH
DECLARE #params XML
SET #params = (
SELECT
#Param1 AS [Param1]
, #Param2 AS [Param2]
FOR XML PATH('params'), TYPE
)
EXEC dbo.LogError #procid = ##PROCID, #params = #params
EXEC dbo.RethrowError
END CATCH
Now, this template does work, except that for each individual procedure, I would have to edit the SET #params section manually.
I could script it out easily enough, but then I would still need to copy it in.
I could leave some sort of placeholder, and then dynamically update each definition w/ a correct SET statement. I could even write a database trigger that listens for CREATE and ALTER PROC statements and have it do this for me automagically.
But what I really want is just to grab the collection of parameters directly and be done with it. Is there any way?
EDIT: getting parameter metadata from the system tables is not enough. For example:
DECLARE #sql NVARCHAR(MAX)
SET #sql =
'SET #params = (SELECT '
+ STUFF((
SELECT ', '+name+' AS '+STUFF(name,1,1,'')
FROM sys.parameters WHERE object_id = ##PROCID
FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)')
,1,2,'')
+ ' FOR XML PATH(''params''), TYPE)'
-- inline execute here....alas, there's isn't a mode for that
EXEC sp_executesql #sql, N'#params XML OUTPUT', #params OUTPUT
This, of course, generates an error, since I haven't declared parameters for the nested scope of sp_executesql. While I could script out the second param of sp_executesql, I can't assign them without explicitly assigning them one at a time by name.
Original question, rephrased: is there another flavor of sp_executesql that inherits variable "context" from the outer scope, that executes in the same frame, that inlines the command, rather than executing in a nested scope?
Why not use sys.dm_exec_sql_text(#sql_handle) to retrieve the SQL command, using sys.sysprocesses.sql_handle?

Firebird constant value

Is it possible to have database wide constants?
What I want is to define a constant like:
UPDATE_CONSTANT = 1
INSERT_CONSTANT = 2
DELETE_CONSTANT = 3
and then use it in for example a trigger like:
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
AS
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
You could use a generator:
SET GENERATOR DELETE_CONSTANT TO 3;
...
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', GEN_ID(DELETE_CONSTANT, 0);
Update: yes, using a generator for this purpose is dangerous, as they can be changed.
However, in FireBird 3.0 Alpha 1 this risk can be eliminated using access rights: Grants access on generators.
I don't think there is an easy way for declaring constants.
I could be done by creating you own DLL for user defined function, and lmake a function for each constant.
I Think the Idea using generators as "global" constants is briliant.
But you can make a "local constant" to make your code a bit more readable:
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
AS
DECLARE VARIABLE DELETE_CONSTANT INTEGER;
BEGIN
DELETE_CONSTANT = 1;
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
Use a single row table with triggers that prevent insertion and deletion from it. Having to read from it certainly does not makes code clearer but it helps to implement such "constants". Also remove write permissions from everybody but sysdba
You can implement some simple preprocesor of yours scripts that converts constants to values..
triggers.presql
#DELETE_CONSTANT = 1
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
triggers.sql
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', 1;
END;

How to trace T-SQL function calls

I'm trying to debug a rather complicated formula evaluator written in T-SQL UDFs (don't ask) that recursively (but indirectly through an intermediate function) calls itself, blah, blah.
And, of course, we have a bug.
Now, using PRINT statements (that can then be read from ADO.NET by implementing a handler for the InfoMessage event), I can simulate a trace for stored procedures.
Doing the same for UDF results in a compile time message:
Invalid use of side-effecting or time-dependent operator in 'PRINT' within a function.
I get the message (PRINT does some stuff like resetting ##ROWCOUNT which definitly is a no-no in UDFs, but how can I trace through the calls? I want to have this trace printed out, so I can study it without getting distracted by stepping through the calls in the debugger...
EDIT: I have tried to use the SQL Profiler (this was a first time one for me), but I can't figure out what to trace for: Although I can get the trace to output the queries sent to the database, they are opaque in the sense that I can't drill down to the Expression-UDFs called: I can trace the actual Stored Procedure invoked, but the UDFs called by this procedure are not listed. Am I missing something? I guess not...
EDIT #2: Allthough the (auto-)accepted answer does trace the function calls - very helpful, thanks - it does not help in finding out what parameters were passed to the function. This, of course, is essential in debugging recursive functions. I will post if I find any sollution at all...
Why not use SQL Profiler with statement level events added?
Edit: Add events for Stored Procedures : SP:Stmt Starting or SP:Stmt Completed
Use variables to debug if needed, i.e. set #debug='i am here'; UDF's, while not technically stored procedures, will get traced with the statement level events.
In the SQL profiler, you need: SP:Starting, SP:StmtStarting, SP:Completed, SQL:BatchStarting. Then, you get every entry, exit from the functions/stored procedures.
alter FUNCTION [dbo].[ufn_mjf](#i numeric(10))
RETURNS numeric(20)
AS
BEGIN
declare #datapoint varchar(10)
set #datapoint = 'hello world'
return #i
END
go
drop table foo
go
create table dbo.foo ( foo_id numeric(10))
go
delete from foo
insert into foo ( foo_id ) values ( 1 )
insert into foo ( foo_id ) values ( 2 )
select foo_id, dbo.ufn_mjf(foo_id) from foo
with this, I get:
SQL:BatchStarting alter FUNCTION [dbo].[ufn_mjf](#i numeric(10))
SQL:BatchStarting drop table foo
SQL:BatchStarting create table dbo.foo ( foo_id numeric(10))
SQL:BatchStarting delete from foo
insert into foo ( foo_id ) values ( 1 )
insert into foo ( foo_id ) values ( 2 )
select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:Starting select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:StmtStarting set #datapoint = 'hello world'
SP:StmtStarting return #i
SP:Completed select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:Starting select foo_id, dbo.ufn_mjf(foo_id) from foo
SP:StmtStarting set #datapoint = 'hello world'
SP:StmtStarting return #i
SP:Completed select foo_id, dbo.ufn_mjf(foo_id) from foo
is that enough for you?
This looks like what you need but it's only available in team/pro versions of Visual Studio.
Use SQL Profiler, I recommend you go overboard on adding events the first time around which will let you get a feel for what you need. Without testing I would add the events for SP:StmtStarted (or Completed or both), SQL:StmtStarted (again Completed or Both).
I second the SQL Profiler suggestion. Take some time to set it up so that only the events you are interested in are logged to cut output size. You can output the trace to a file - I have frequently then loaded that file back into a table to enable analysis. (extremely handy for performance analysis, though no doubt someone will tell me that 2008 has this all built in somwehere...)
Sometimes you won't have permissions to run SQL Profiler as it does slow the server down - ask your DBA to grant you permission on your Dev server. They shouldn't have any problems with that.
Well in the past I have had to take typical values that would be in the UDF and then run just the udf part in a separate query window as straight SQL not a udf using the typical values as variables set with a declare and a set statement. If it is run from a table instead of having only one value, I would set up a temp table or table variable with the input values and then run them through the sql in the UDF (but again as straight SQL not a UDF) through a cursor. By running straight SQL you could have print statements in it to see what is happening. I know this is a pain, but it works. (I go through a simliar process when creating/debugging triggers, setup #inserted and #deleted with my test values and then test the code I intend to put into the trigger, then global replace the # with nothing and add the create trigger code.)
Maybe you can use SQL CLR to do the tracing as described here
How to log in T-SQL
Can you take your function, and make a second copy of it, but returning a table type with an additional column for your debug information.
For example, the mySum function below
CREATE FUNCTION mySum
(
#param1 int,
#param2 int
)
RETURNS INT AS
BEGIN
DECLARE #mySum int
SET #mySum = #param1
SET #mySum = #mySum + #param2
RETURN #mySum
END
GO
SELECT dbo.mySum(1, 2)
Would turn into
CREATE FUNCTION mySumDebug
(
#param1 int,
#param2 int
)
RETURNS #myTable TABLE
(
[mySum] int,
[debug] nvarchar(max)
)
AS
BEGIN
DECLARE #debug nvarchar(max)
SET #debug = 'Declare #mySum variable. '
DECLARE #mySum int
SET #debug = #debug + 'Set #mySum = #param1(' + CONVERT(nvarchar(50), #param1) + ') '
SET #mySum = #param1
SET #debug = #debug + 'Add #param2(' + CONVERT(nvarchar(50), #param2) + ') to #mySum(' + CONVERT(nvarchar(50), #mySum) + ') '
SET #mySum = #mySum + #param2
SET #debug = #debug + 'Return #mySum variable. '
INSERT #myTable (mySum, debug) VALUES (#mySum, #debug)
RETURN
END
GO
SELECT mySum, debug FROM dbo.mySumDebug(1, 2)
Not an ideal solution, but useful just to return some text to help track down a bug.
I use SQL SPY which does what you are looking for and more.
SQL SPY
SQL SPY Feature Documentation
SQL SPY's Incoming SQL Sniffer shows the incoming SQL code of each connection (Includes DDL and DML statement tracking)
This feature is designed for MS SQL Server 2005\2008, but will work with MS SQL Server 2000 in limited scope. It has the ability to record and report on Incoming SQL. How to use the features: See
Disclosure: I am part of the SQL SPY team.