I am trying to use the spyProcedure , but it is not working as expected.
Below is the scenario please help me where am I doing wroing?
**CREATE PROC [dbo].[proc3](#id int , #marks int out)
as
begin
select #marks = marks from student where id=#id;
end**
CREATE PROC [dbo].[proc4](#id int , #marks int )
as
begin
print 'inside proc4 id ' + cast(#id as varchar) + ' marks ' + cast(#marks as varchar);
update student set marks = #marks where id=#id;
end
**CREATE PROC [dbo].[proc6](#id1 int , #id2 int)
as
begin
declare #marks int;
EXEC tSQLt.SpyProcedure 'proc3' , 'SET #marks = 80';
set #marks = #marks + 20;
print ' marks = ' + cast(#marks as varchar);
EXEC proc4 #id = #id2 , #marks = #marks;
end**
EXEC proc6 #id1 = 1 , #id2 = 2;
GO
When I call the above , it is not able to execute/call the " EXEC proc4 #id = #id2 , #marks = #marks;" but when I comment "EXEC tSQLt.SpyProcedure 'proc3' , 'SET #marks = 80'; " then it is able to be executed.
Can you please help me where am I doing wrong and how to execute it correctly.
Thank you.
I don't think you have fully grasped how to use tSQLt from your code example above.
To start with to run a basic tSQLt test you need to do a few things.
All tests need to be in a test class.
EXEC tSQLt.NewTestClass 'MyTestClass'
GO
All tests must start with the key word 'test'
CREATE PROCEDURE [MyTestClass].[test_MyFirstTest]
AS
BEGIN
EXEC tSQLt.AssertEquals 1,1, '1 does in fact equal 1'
END
To run a test you a test you use the tSQLt test runner
EXEC tSQLt.Run '[MyTestClass].[test_MyFirstTest]'
Using SpyProcdure is an advanced feature and should really only be attempted once you have a clear understanding of the basics. If you are experience with TDD and mocking frameworks then it shouldn't be a massive leap to getting SpyProcedure to work.
If you feel you are comfortable with the basics first try to use SpyProcedure to record what parameters the spied on procedure was called with using SpyProcedureLog (see the documentation at tSQLt.org) and then try returning a resultset. What you are attempting in your example above (to pass back a value in a variable) is about as hard as you can get with tSQLt.
Related
I am attempting to spy a procedure with an output parameter. This procedure has two parameters, one input parameter and one output parameter.
The input parameter has a default value of NULL.
CREATE PROCEDURE spExampleProcedure
#INPUTPARAM DATETIME = NULL,
#OUTPUTPARAM INT = NULL OUTPUT
AS
....
I'm attempting to test a procedure that is calling spExampleProcedure. spExampleProcedure is called multiple times with a different #INPUTPARAM. I want to check that param and return a different value based on the input. (A more advanced sort of mock.)
EXEC tSQLt.SpyProcedure 'dbo.spExampleProcedure',
'SET #OUTPUTPARAM = CASE WHEN #INPUTPARAM IS NULL THEN 1 ELSE 2 END'
This is not working. I would really like to be able to fake/spy a procedure like I do a function because It would really help when a stored procedure is called multiple times.
An option that I've considered is converting my spExampleProcedure to a Function but that would only avoid my issue instead. Looking at spy procedure, I see no reason why my setup should not be working beside maybe the fake procedure it creates might not have a default value of null.
The posted example should work as pointed out by Sebastian Meine. I wanted to elaborate on why my test was not working in case that ends up helping somebody else.
My issue was related to my test data setup.
Consider:
CREATE PROCEDURE spExampleProcedure
#INPUTPARAM DATETIME = NULL,
#OUTPUTPARAM INT = NULL OUTPUT
AS
....
And
EXEC tSQLt.SpyProcedure 'dbo.spExampleProcedure',
'SET #OUTPUTPARAM = CASE WHEN #INPUTPARAM IS NULL THEN 1 ELSE 2 END'
Procedure Under Test:
CREATE PROCEDURE spExampleProcedureUnderTest
#ID INT
AS
BEGIN
DECLARE #EXAMPLEVAR DATETIME, #OUTPUT
SELECT #EXAMPLEVAR = VAR FROM ExampleTable WHERE ID = #ID
EXEC spExampleProcedure #OUTPUTPARAM = #OUTPUT OUTPUT
EXEC spExampleProcedure #EXAMPLEVAR, #OUTPUT OUTPUT
...
My Test Procedure was faking ExampleTable but not putting a value in for VAR.
EXEC tSQLt.FakeTable 'dbo.ExampleTable'
INSERT INTO ExampleTable (ID) VALUES (1)
EXEC tSQLt.SpyProcedure 'dbo.spExampleProcedure',
'SET #OUTPUTPARAM = CASE WHEN #INPUTPARAM IS NULL THEN 1 ELSE 2 END'
EXEC spExampleProvedureUnderTest 1
Instead of
EXEC tSQLt.FakeTable 'dbo.ExampleTable'
INSERT INTO ExampleTable (ID, VAR) VALUES (1, '2018-06-01')
EXEC tSQLt.SpyProcedure 'dbo.spExampleProcedure',
'SET #OUTPUTPARAM = CASE WHEN #INPUTPARAM IS NULL THEN 1 ELSE 2 END'
EXEC spExampleProvedureUnderTest 1
EMPHASIS ON line 2 of each. Notice that I added a value in my insert.
Effectively, my spied Procedure was being called both times with NULL. BECAREFUL with data coming from Faked Tables. Faked tables remove constraints so it's easy to put a null into a table that would otherwise not allow it.
Hi can you help me please?, That way I can return the result of a query string in a variable inside a function with "execute"?.
for example
create function my _funcion(in # num_celular char (20), in # str_comando char (120))
returns char (120)
- on exception resume
as
begin
set # exec_qry = Select 1 from dummy
execute (# exec_qry)
return (# the return value of the query "execute")
end
go
thanks
Try this way:
create function my_funcion(#num_celular char (20), #str_comando char (120))
returns char(120)
as
begin
DECLARE #Result char(120)
execute('select #Result = ''Aaa'' ' )
RETURN #Result
end
go
Result of below query
select my_funcion('AA','BB')
is
Aaa
Ok, thanks for answering my question, but look at the issue is much more complex because we are running a Dynamic Sql, formed in while loop to the query is then stored in one string variables and running it, the problem are the function promptly made as follows and is attached below.
create function ms_trafico.fn_mov_cambia_tag( in #num_celular char(20), in #str_comando char(120) )
returns char(120)
--on exception resume
as
begin
set nocount on
set temporary option string_rtruncation='OFF'
--aplican para isql
--set option isql_print_result_set=ALL
--set option isql_show_multiple_result_sets=On
declare #comando varchar(100)
declare #exec_qry varchar(500)
declare #cadena varchar(60)
declare #tag char(15)
declare #columna char(20)
declare #pos int
declare #strCadFinal char(150)
declare #strFono char(20)
set #comando = trim(#str_comando)
set #strFono = trim(#num_celular)
set #exec_qry='select XXX '
set #pos = 1
print "comando " + #comando
while(#pos <= locate(#comando,'<',-1))
begin
set #tag = substr(#comando,locate(#comando,'<',#pos),CHARINDEX('>', SUBSTR(#comando,locate(#comando,'<',#pos),length(#comando))))
set #pos = locate(#comando,'<',#pos) + 1
select atributo_campo into #columna from TB_MOV_MAPEO_COMANDOS where parametro = #tag
set #cadena="replace(XXX,'"||trim(#tag)||"',"||trim(#columna)||")"
set #exec_qry=replace(#exec_qry,'XXX',#cadena)
end
set #comando="'"||trim(#comando)||"'"
set #exec_qry=replace(#exec_qry,'XXX',#comando)
set #exec_qry=#exec_qry||' as comando INTO #strCadFinal FROM VM_ALL_SERVICES_MOVIL where num_celular=#strFono'
execute (#exec_qry)
return (#strCadFinal) "The result of my query with execute method"!!!!!!!!!!
end
go
Thanks for you attention.
I have following scalar function but the problem is that it doesn't return actual result but returns a query.
Could somebody guide me on it what I am missing here or making wrong?
I have formed the following function
ALTER function test(#QueId int, #Answer varchar(250)) RETURNS VARCHAR(500) AS
BEGIN
DECLARE #STR varchar(4000)
DECLARE #QRY AS VARCHAR(1000)
SELECT #QRY=
'SELECT #str = COALESCE(#str + '';'','''') + AnswerText '+
'FROM SurveyQuestionAnswerTypes WHERE AnswerType=(Select AnswerType From SurveyQuestions Where QuestionID=' +
CAST(#QueId AS VARCHAR(4)) + ')AND AnswerValue in (' + replace(#Answer,'^',',') +')'
--EXEC sp_executesql #QRY, '#STR VARCHAR(4000)', #STR
RETURN #QRY
END
Instead of returning a result it returns
SELECT #str = COALESCE(#str + ';','') + AnswerText
FROM SurveyQuestionAnswerTypes
WHERE AnswerType = (Select AnswerType
From SurveyQuestions
Where QuestionID=25)AND AnswerValue in (3,4,5,6)
Well - it returns the query because you told it to do so!
Look at your code: you have commented out the line that would actually execute the dynamic SQL (EXEC sp_executsql .....), and instead you're returning the query (#QRY) as a string:
--EXEC sp_executesql #QRY, '#STR VARCHAR(4000)', #STR
RETURN #QRY
Just change that to execute the query instead of returning its string representation....
Try this syntax, this might work for you:
Exec (#QRY)
You cannot execute "exec" or "sp_executesql" nor can execute a stored procedure in a function.
you can refer this
blog
OR this SO question
I am looking for a utility that will convert Oracle SQL to a string that can executed dynamically.
Edit:
Yes, consider this simple SQL
SELECT * FROM TABLE
WHERE COLUMN_NAME = 'VALUE'
I have a utility which for T-SQL which converts the above SQL to a synamic SQL as follows:
BEGIN
DECLARE #Exe_String VarChar(2000)
DECLARE #Qt Char(1)
DECLARE #Cr Char(1)
SET #Qt = Char(39)
SET #Cr = Char(10)
SET #Exe_String = 'SELECT * FROM TABLE ' + #Cr
SET #Exe_String = #Exe_String + 'WHERE COLUMN_NAME = ' + #Qt + 'VALUE' + #Qt + '' + #Cr
PRINT #Exe_String
--Execute (#Exe_String)
END
Granted that the code generated good probably be better, yo get the idea, I hope.
I'm looking for the same type of conversion for Oracle SQL.
Here is a tool that I have used a couple of times. You will have to change the output a little to get it to run but it sure beats having to figure out how to escape all the single ticks.
Sql Tuning
After you click on the link it will take you right to the site and a page with sample SQL. Click the "Static SQL to Dynamic SQL" button and you can see how it works. Then input your own sql you want converted and click the button again. Remove the extra tick (') marks in the end and beginning of each line with the exception of the first and last line and pipes (|) don't need to be there either. Hope this helps.
As a raw translation of your T-SQL to PL/SQL
DECLARE
Exe_String VarChar(2000);
Qt CONSTANT Char(1) := CHR(39);
Cr CONSTANT Char(1) := CHR(10);
BEGIN
exe_string := 'SELECT * FROM TABLE '||Cr;
exe_string := exe_string ||
'WHERE COLUMN_NAME = ' || Qt || 'VALUE' ||Qt || '' ||Cr;
dbms_output.put_line(exe_string);
--
EXECUTE IMMEDIATE exe_string;
END;
The obvious difference is that in Oracle the concatenation operator for strings is || rather than +.
Personally, I have a little string manipluation package (let's call it pstring) that I'd use in a case like this - includes functions like enquote(string), standard constants for newline,tab,etc and the ability to do C-style text replacement.
exe_string :=
pstring.substitute_text('SELECT * FROM %s \n WHERE %s = %s',
table_name,column_name,pstring.enquote(value));
Have you considered using bind variables - i.e. :value - rather than dealing with escaping all the internal quotes? It's a good defence against SQL injection.
Obviously there's some difficulty if you have varying numbers of variables (you need to use DBMS_SQL to link them to the statement rather than a simple EXECUTE IMMEDIATE) but for your simple case it would look like this.
PROCEDURE (table_name IN VARCHAR2, column_name IN VARCHAR2)
IS
Exe_String VarChar(2000);
BEGIN
exe_string :=
pstring.substitute_text('SELECT * FROM %s \n WHERE %s = :value',
table_name,column_name);
dbms_output.put_line(exe_string);
--
EXECUTE IMMEDIATE exe_string USING pstring.enquote(value);
END;
Although of course you have to do something with the results of your SQL.
EXECUTE IMMEDIATE exe_string INTO lresult USING pstring.enquote(value);
Which is difficult when the shape of the table may differ - again, you have to look at Type 4 dynamic SQL (DBMS_SQL).
I have the following Stored Procedures
create or replace PROCEDURE WEB_AC
(
v_iDocid IN NUMBER DEFAULT NULL ,
v_valor IN VARCHAR2 DEFAULT NULL ,
v_campo IN VARCHAR2 DEFAULT NULL ,
v_error OUT NUMBER
)
AS
v_campoid NUMBER(5,0);
v_tipodato VARCHAR2(50);
v_DOCTYPE NUMBER;
v_tabla VARCHAR2(50);
v_procedure VARCHAR2(70);
BEGIN
v_error:= 0;
IF v_valor IS NULL
OR v_valor IS NULL
OR LENGTH(TRIM(v_valor)) = 0 THEN
BEGIN
v_error:= 3;
END;
else
Begin
bEGIN
SELECT campoid,
doctype,
tipodato
INTO v_campoid,
v_DOCTYPE,
v_tipodato
FROM TiposDocumento t
, DIGITALIZAMAIN d
, CatCamposDocumento c
where
c.tabla=t.tabla and
nombre=v_campo and
doctype = TipoDocumentoID and
docid = v_iDocid AND
Mostrar = 1;
EXCEPTION
WHEN OTHERS THEN
v_campoid := 0;
END;
--select #campoid
IF v_campoid != 0 THEN
Begin
EXECUTE IMMEDIATE 'BEGIN ABANCE3.WEB_UPDOC' || TRIM(TO_CHAR(v_DOCTYPE )) || 'C' || TRIM(TO_CHAR(v_campoid)) ||'(' ||
TO_CHAR (v_iDocid)||' , '||CHR(39)||v_valor||CHR(39)||',:2);END;'
USING out v_error;
END;
END IF;
end;
end if;
END;
And
create or replace PROCEDURE WEB_UPDOC1C6(v_idreg NUMBER,v_valor VARCHAR2,v_temp OUT NUMBER)
AS
v_sys_error NUMBER := 0;
BEGIN
BEGIN
SELECT count(*)
INTO v_sys_error
FROM DOC1
where DOCID = v_idreg;
EXCEPTION WHEN OTHERS THEN v_sys_error:=0;
END;
IF v_sys_error > 0 THEN
BEGIN
BEGIN
UPDATE DOC1
SET DESCRIPCION = v_valor
WHERE DOCID = v_idreg;
EXCEPTION WHEN OTHERS THEN v_sys_error:=0;
END;
IF v_sys_error = 0 THEN v_temp:=0 ;
ELSE v_temp:=1 ;
END IF;
END;
END IF;
END;
and I'm calling them from an Application with this code:
Friend Function ActualizaCampos(ByVal iDocID As Long, ByVal valor As String, ByVal Campo As String, ByVal ProyectoID As Integer) As String
Dim mstrCS as String = "Here goes the connection String to my server"
Dim db As Database
Dim dbCW As DbCommand
Dim iValor As String = "0"
Select Case Me.TipoBD
Case GlobalDef.eTipoBD.Oracle
db = New OracleDatabase(mstrCS)
dbCW = db.GetStoredProcCommand(WEB_AC, iDocid, valor, Campo, 0)
db.ExecuteNonQuery(dbCW)
Case GlobalDef.eTipoBD.SQLServer
db = New SqlDatabase(mstrCS)
dbCW = db.GetStoredProcCommand(WEB_AC, iDocid, valor, Campo)
iValor = db.ExecuteScalar(dbCW).ToString()
End Select
Return iValor
End Function
In this example the WEB_AC SP always execute the sp WEB_UPDOC1C6
I have two problems with this.
First problem: At some point in the application I have the valor parameter (of the visual Basic Function) as a string with spaces that is something like "some string with spaces". When this happens, the stored procedure don't update the table. If I execute the SP directly in the DB (with SQL Developer) all works fine. I know it has something to do with the string missing some quotes(') but I haven't make it work yet. Some ideas on this?
Second problem: Sometimes, when debuging the application, if I interrupt the execution, I start getting the ORA-24338 'statement handle not executed' error for hours every time I try to execute it again. I believe it has something to do with an open transaction. But honestly, as I'm new in working with Oracle, I really have no idea what the problem could be.
Can you help me?
UPDATE: I have found the ORA-24338 real reason. It was another SP that was causing the error. When i found the solution to my other problem I'll post it all here.
I'd suggest that you replace your WHEN OTHERS clause with one that specifically names the erros that you are expecting, or you raise the error after handling it anyway. WHEN OTHERS is a bit contraversial as it is notorious for hiding the real problem.