I have stored procedure which conditionally execute nested stored procedure.
In unit test, I need to check if this nested stored procedure was executed.
I tried tSQLt.SpyProcedure, but it doesnt seems to work the way i want.
content of my unit test
-- Assembly
exec tSQLt.SpyProcedure 'procedureName', 'raiserror(''procedureName was fired'',16,1)'
-- Assert
exec tSQLt.ExpectException 'procedureName was fired'
-- Action
exec masterProcedureName -- triggers procedureName
but tsqlt.run 'unitestName' returns
failed: (Failure) Expected an error to be raised.
Do you have any idea ?
While, as you mentioned, your approach works, I suggest you use the ..._spyprocedurelog table instead. It’ll allow you to catch multiple executions as well as the parameters passed each time. And if you at some point add error handling to the outer procedure, this will still work.
Check out the example in the SpyProcedure documentation.
My apologies, following code does work i had mistake somewhere else.
Related
I need to have a procedure to calculate count of something and insert it into another table but get error
ORA-01006:bind variable does not exist.
Here is my code:
Insert part is not be executed and jumps to exception instead.
Your dynamic SQL call is
EXECUTE IMMEDIATE v_sql USING v_result;
This is the syntax for passing a parameter into the dynamic statement. But your code doesn't take any parameters, because you have concatenated them in the string. Therefore, the code hurls ORA-01006.
What you need to do instead is provide a variable for the result set to be return into. So the call should be
EXECUTE IMMEDIATE v_sql INTO v_result;
The syntax for EXECUTE IMMEDIATE is comprehensively covered in the PL/SQL guide. You should bookmark the Oracle documentation for future reference.
I have 2 stored procedures where the first one calls the second one within a transaction. The second procedure should never be called directly, but only from within its parent.
Currently, to check if this is the case I'm doing the following in the second procedure:
DECLARE #inTran bit;
IF ##TRANCOUNT > 0
SET #inTran= 0
ELSE
SET #inTran= 1
Is this correct? Is there a better way to do this?
If you are just looking for a casual way to prevent inadvertent execution of the proc on its own. You could also check ##NESTLEVEL - this will be at least 2 if called from another proc.
CREATE PROCEDURE Child
AS
IF ##NESTLEVEL < 2 OR ##TRANCOUNT = 0
THROW 50000, 'Child proc should be called from Parent', 1;
Or you could have the parent proc set a value read by SESSION_CONTEXT() in the child proc.
None of these will prevent the proc not being run as intended by someone determined to circumvent the restrictions though. They will just guard against accidental misuse.
There is no reliable way of doing this. Checking ##trancount only gives you information if you are in a transaction or not, and someone could do this:
BEGIN TRAN
EXECUTE nested_proc_directly
In this case, the tran count in the proc would b greater than 0. And as others have said you cannot the call stack. So, sorry.
I'm reading between the lines here a little bit, and guessing that the actual question is how to prevent Procedure2 from being run by any process except a call from Procedure1.
If this has to be as close to totally locked down as possible, create a dedicated service account to run these procedures, or their associated job(s), and then only grant EXECUTE permissions on Procedure2 to that dedicated account.
If "pretty locked down" is good enough, only grant EXECUTE permissions on Procedure2 to the service account you have running your jobs in production. At least that would keep stray users from firing it off willy-nilly.
Another thought would be to create an SSIS package with two Execute SQL Tasks in it, with the first containing all the code in Procedure1 and the second containing all the code in Procedure2, then do away with the procs and run the package instead. I don't care for embedding code in packages, though, because maintenance is irritating.
You can use ##PROCID, for this. The only problem is that you need to pass the parameter by input.
CREATE PROCEDURE usp_Test1(#id As int)
AS
PRINT #id
PRINT OBJECT_NAME(#id)
GO
CREATE PROCEDURE usp_Test2
AS
EXEC usp_Test1 ##PROCID
GO
EXEC usp_Test2
GO
output
1054730910
usp_Test2
So, I'm new to MS SQL (have been using oracle for the last 5-7 years) and this should be very a straight forward thing to do, so I reckon I'm missing something very simple.
(I've tried following the examples here: http://technet.microsoft.com/en-us/library/ms190669(v=SQL.105).aspx)
So, I create the following stored procedure to query a table (this is a very simple and pointless procedure but I can't proceed with my more complex procedure until I resolve this problem)
create procedure sp_getTransactions
as
select * from MyTransactions;
I then try to execute this procedure
execute dbo.sp_getTransactions
(I've tried without the dbo. and get the same error)
This gives me the very helpful error
Incorrect syntax near the keyword 'BEGIN'.
Now, maybe I'm crazy but I don't see a begin statement anywhere in my procedure (I've tried adding one to no avail).
Can anyone give me some pointers here?
Thanks
Actually, the problem as it turns out is the client I was using. I was executing the sql scripts using Oracle's SQLDeveloper with the MSSQL jTDS driver. It seems this driver works fine for the most part, but when it comes to running stored procedures there's a bug.
I guess the execute statement isn't parsed properly by the plugin when being set to the server
Check like this:
CREATE PROCEDURE sp_getTransactions
AS
BEGIN
SELECT * FROM MyTransactions;
END
In execute dbo.sp__getTransactions statement, you used 2 _, but in CREATE PROCEDURE statement it has only one. Change to dbo.sp_getTransactions and try to execute.
When trying to validate a user supplied GUID within a stored procedure a simple approach was used; take the user input as a CHAR(36) then explicitly CAST it as a UNIQUEIDENTIFIER within a TRY CATCH. The CATCH then bubbles the error with a custom error description using a RAISERROR.
Running the stored procedure manually everything performs as expected and the error is raised.
Create a tSQLt test to call the unit (the procedure with GUID validation) and handle the error that is output and compare with the expected error continually fails with a transaction error; tSQLt has detected an error and handled within the tSQLt framework.
This suggests to me that the severity of a failure to CAST to a different datatype is being handled by tSQLt and it is preventing the TRY/CATCH within the stored procedure to handle it. Much like nested procedures sometimes ignore the TRY/CATCH within the child procedure and bubble up to the parent procedure; example being if the child proc. references a table that doesn't exist.
Has anyone had a similar issue? Just simply to validate my current line of thinking.
I've removed the test and it's being tested elsewhere, but this has caused me a 'hole' it my DB unit tests.
Finally, I think I should mention that I know I can perform a different validation on a supplied CHAR parameter, other than a CAST, and raise an error that way, but this is a tSQLt query and not a tSQL query.
EDIT
Example of the code:
#sGUID is a CHAR(36) and is a parameter passed to the procedure.
BEGIN TRY
SELECT CAST(#sGUID AS UNIQUEIDENTIFIER)
END TRY
BEGIN CATCH
RAISERROR('Invalid GUID format',16,1)
END CATCH
The SELECT line never triggers the CATCH tSQLt appears to intervene before hand and throws the ROLLBACK transaction error.
When you call RAISEERROR(), you're terminating the transaction that tSQLt is running --> hence the transaction error you're seeing.
To improve this for the purpose of unit testing, one option you might consider would be to replace the RAISEERROR() statement with a call to a custom stored procedure that only contains RAISERROR(). That way, you can unit-test that stored procedure seperately.
BEGIN TRY
SELECT CAST(#sGUID AS UNIQUEIDENTIFIER)
END TRY
BEGIN CATCH
EXEC dbo.customprocedure
--RAISERROR('Invalid GUID format',16,1)
END CATCH
I would like to call a stored procedure or user-defined function that returns a dynamic table that is created via a pivot expression. I don't know the number of columns up front.
Is this possible? (I am not interested in temporary tables)
You can do that via stored procedure as it can return any kind of table, question is what are you trying to achieve and what will you do with data that you have no idea about?
This cannot be done with functions (as the returned table structure must be pre-defined), but it can be done with a stored proceed. Some psuedo-code:
CREATE PROCEDURE Foo
As
DECLARE #Command
SET #Command = 'SELECT * from MyTable'
-- For debugging, work in an optional PRINT #Command statement
EXECUTE (#Command)
RETURN 0
When you run stored procedure Foo, it builds your query as a string in #Command, and then dynamically executes it without knowing anything about what is being queried or returned, and the data set returned by that EXECUTE statement is "passed back" to the process that called the procedures.
Build your query with care, this stuff can be really hard to debug. Depending on your implementation, it might be a source of SQL injection attacks (remember, the stored procedure really doesn't know what that dynamic query is going to do). For quick stuff, EXECUTE() works fine, but for safer and more useful (if elaborate) solutions, look into sp_ExecuteSQL.
Yes, you can do this from a Stored Procedure, but not from a user-defined Function. It is worth looking into the Table Value Function, I believe you can also return a dynamic table from there, but I have not used that myself.