PHP - execute Sql Server stored procedure with 2 output parameters - sql-server

I'm working with a third-party company in order to integrate an web application with their Windows application.
To write the necessary data into their database they gave me the following procedure:
Declare #SavedOK int, #Message varchar(8000)
Exec pbProcessTransactions
#SavedOK output,
#Message output
I have to execute it with PHP, this is how I've written my application, and for this I used something that I've found on Micrsoft's documentations.
My code which executes the stored procedure with 2 output parameters is:
$tsql_callSP = "{call pbProcessTransactions(?, ? )}";
$SavedOK = '';
settype($SavedOK, "integer");
$Message = '';
settype($Message, "string");
$raspuns = '';
$params = array(
array($SavedOK, SQLSRV_PARAM_INOUT),
array($Message, SQLSRV_PARAM_INOUT)
);
/* Execute the query. */
$stmt3 = sqlsrv_query( $conn, $tsql_callSP, $params);
if( $stmt3 === false ) {
echo "Error in executing statement 3.\n";
die( print_r( sqlsrv_errors(), true)); }
/* Display the value of the output parameters. */
while ($obj=sqlsrv_fetch_object($stmt3)) {
// SET PARAMETERS - SET TERMS
echo $obj->SavedOK;
echo $obj->Message;
}
/*Free the statement and connection resources. */
sqlsrv_free_stmt( $stmt3);
/*WHERE SavedOK and Message are the 2 output parameters which will display various info depending if the procedure executes normally or not, SavedOK will display 1 or 0 , 1 if it's ok 0 if not and Message will display the error message if SavedOK is 0 */
My problem is that the statement is always false, it always displays "Error in executing statement 3" and I cannot see the output, if SavedOK is 1 or 0, which is the message.

Related

How to return result from a stored procedure to TASK RETURN_VALUE in snowflake?

I would like to return logging and status messages from a stored procedure to the TASK that calls it.
create or replace procedure status_return()
returns string not null
language javascript
as
$$
var result_status = 'The return status and debug information in string format';
return result_status; // Statement returned for info/debug purposes
$$;
I would like to pass the result from stored procedure call status_return() back to the task
-- Create a task that calls the stored procedure every hour
create or replace task call_SP
warehouse = SMALL
schedule = '1 minute'
as
call status_return();
When I execute TASK_HISTORY to view RETURN_VALUE is always empty.
select *
from table(information_schema.task_history(SCHEDULED_TIME_RANGE_START => dateadd(hours, -5, current_timestamp()) ,
TASK_NAME => 'call_sp'));
How can I view the result of a stored procedure in task_history for SUCCESS, FAILURE, or ERRORS?
I have tried creating a task in the following way, but I was unsuccessful and it return with errors.
create or replace task call_SP
warehouse = EDS_SMALL
schedule = '1 minute'
as
call system$set_return_value(call status_return());
Can I use Javascript in Tasks? To store the result of a stored procedure call into a variable and return it back to the TASK result
In order to be able to get a RETURN_VALUE in your TASK_HISTORY you have to set the return_value in your stored procedure using call system$set_return_value().
Examples can be found in snowflake documentation.
This is what it should looks like if you want the return_value field of the task_history to return your result status var when your task is launched :
create or replace procedure status_return()
returns string not null
language javascript
as
$$
var result_status = 'The return status and debug information in string format';
var rv_stmt = snowflake.createStatement({sqlText:`call system$set_return_value('` + result_status + `');`});
var rv_res = rv_stmt .execute(); // Set return_value
return result_status; // Statement returned for info/debug purposes
$$;

Insert new rows into tables?

I am trying to execute a stored procedure which performs an Insert action but I am getting an error (as shown in the screenshot). Following is my PB code and the subsequent one is the declaration of my params in the stored procedure.
DECLARE sp_insert_EndorsementUnderlying_AP PROCEDURE FOR
#EndNo = :endnum1,
#PolicyId = :policyid1
USING SQLCA;
EXECUTE sp_insert_EndorsementUnderlying_AP;
CHOOSE CASE SQLCA.sqlcode
CASE 0
// Execute successful; no result set
COMMIT;
CASE ELSE
MessageBox ("INSERT of New Endorsement Rows Failed", &
string (SQLCA.sqldbcode) + " = " + &
SQLCA.sqlerrtext)
RETURN
END CHOOSE
Stored procedure declaration:
ALTER PROCEDURE [dbo].[sp_insert_EndorsementUnderlying_AP]
#EndNo SMALLINT,
#PolicyId INT
AS
BEGIN
Error message:
[enter image description here][1]
[1]: https://i.stack.imgur.com/error msg screen shot
The syntax in PB to declare a procedure is:
DECLARE **PBPROCNAME** PROCEDURE FOR **DATABASEPROCNAME**
#parm1 = :pbvariable1, #parm2 = :pbvariable2 USING SQLCA;
Which is not what you have in your example. Can't comment on the error since your link does not work.
Try this one (note the slight difference procedure name - this is not compulsory but it helps make the difference between both worlds):
DECLARE pb_insert_EndorsementUnderlying_AP PROCEDURE FOR sp_insert_EndorsementUnderlying_AP
#EndNo = :endnum1,
#PolicyId = :policyid1
USING SQLCA;
EXECUTE pb_insert_EndorsementUnderlying_AP;
CHOOSE CASE SQLCA.sqlcode
CASE 0
// Execute successful; no result set
COMMIT;
CASE ELSE
MessageBox ("INSERT of New Endorsement Rows Failed", &
string (SQLCA.sqldbcode) + " = " + &
SQLCA.sqlerrtext)
RETURN
END CHOOSE

Dapper Distinguishing Return Value and Result Set

I am using Dapper's QueryMultipleAsync method to read multiple result set. My stored procedure checks some conditions (for example a user might be trying to get someone else's data by sending an Id to my API) to determine whether the data should return or not.
I, on C# side, first need to read the return value (not result set) to determine if data is returned or SP simply returned 3 (which means insufficient rights). To illustrate the case:
IF #UserRole < 10
BEGIN
RETURN 3; -- Insufficient rights.
END
IF #IsCurrentUserOwner = 0
BEGIN
RETURN 5; -- Not owner.
END
-- Get users.
SELECT
Id
, [Name]
, LastName
FROM
Users
-- WHERE ...
-- Get chat messages.
SELECT
*
FROM
ChatMessages
-- WHERE ...
I know that output and return values are written at the end of reader so I can only read return parameter when all the data (result set) is read. So, I always have to read the result set first then return/output parameters.
What if my SP looked like this:
-- ...some code above...
IF #IsCurrentUserOwner = 0
BEGIN
RETURN 5; -- Not owner.
END
DECLARE #RType TINYINT = NULL
-- some other code here to get #RType value here...
SELECT #RType -- To make this a result so QueryMultiple's reader can read this.
-- ...some other code to get users and chat messages...
To describe the problem:
#RType variable could be 5 as well as SP's return value. When I read the result first (because output/return parameters are at the end of reader), how do I know the value I just read (which is 5 in this case) is the #RType or return value? (they are convertible)
This is how my C# code roughly looks like:
var parameters = new DynamicParameters();
parameters.Add("#RetVal", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
var dbResult = await Context.SqlConnection.QueryMultipleAsync(sql: "my_sp_name", param: parameters, commandType: CommandType.StoredProcedure)
// Some code here...
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5)
{
return "You are not allowed to see this data";
}
How do you recommend me to handle the case?
Your C# code uses async - await (QueryMultipleAsync) and currently Dapper doesn't support for Async methods that no perform a SELECT statement.
There is an open issue about that in Github:
https://github.com/StackExchange/Dapper/issues/591
Option 1
You can split your stored procedure into 2 parts:
1. Check the Return value.
2. Getting the select statements.
Option 2
ALTER PROCEDURE dbo.sp
... your params..
#Output INT OUTPUT
AS
IF #UserRole < 10
BEGIN
SET #Output = 3; -- Insufficient rights.
END
IF #IsCurrentUserOwner = 0
BEGIN
SET #Output = 5; -- Not owner.
END
-- Get users.
SELECT
Id
, [Name]
, LastName
FROM
Users
-- WHERE ...
-- Get chat messages.
SELECT
*
FROM
ChatMessages
-- WHERE ...
Option 3
Use ADO.NET
I wasn't really clear on your question, but the snippet below should work. Since you are defining the #RetVal as ParameterDirection.ReturnValue you should be able to access it before accessing the actual readers. I was able to verify this works in SQL SERVER.
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5 || returnValue == 3)
{
return "You are not allowed to see this data";
}
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
here is my stored procedure
ALTER PROCEDURE mysp_TestOne
#p1 int
AS
BEGIN
SET NOCOUNT ON;
if #p1 > 0
return 5;
SELECT 1 F1
union
Select 2 F1;
SELECT 1 F1
union
Select 2 F1;
END
GO
and more complete snippet of c#;
using (SqlConnection cn = new SqlConnection(connectionString))
{
var sql = #"mysp_TestOne";
var parameters = new DynamicParameters();
// #p1 triggers whether to return query results or just the returnvalue
parameters.Add("p1", 0, direction: ParameterDirection.Input);
parameters.Add("#RetVal", 0, direction: ParameterDirection.ReturnValue);
var dbResult = await cn.QueryMultipleAsync(sql, parameters, commandType: CommandType.StoredProcedure);
var returnValue = parameters.Get<int>("#RetVal");
if (returnValue == 5 || returnValue == 3)
{
return "You are not allowed to see this data";
}
using (var reader = dbResult)
{
var rType = reader.Read<byte>(); // <----- How to know if I read #RType or just return value of SP since they are convertible to each other?
var users = reader.Read<User>();
var chatMessages = reader.Read<ChatMessage>();
// ...
}
}
HTH

Validation - If - SQL Server

I am trying to send a return message to front end. If the parameter which is passed, if it is not in format like
'A-1213-465-798-01'
It should reply back 1. But I am missing out the validation. please help.
'A-1213-465-798-01'
IF #OptParam3 not like '[_-_-_-_-_]'
Begin
Set #Return_Message = '1' -- Validation 'Invalid code
Print 'Error: Invalid Code'
Return
End
Assuming this is a stored procedure you probably want something along the lines of this
IF OBJECT_ID('sp_demo') > 0 DROP PROC sp_demo
go
CREATE PROC sp_demo (#OptParam1 varchar(50),#OptParam2 varchar(50),#OptParam3 varchar(50))
AS
IF #OptParam3 not like '_-____-___-___-__'
Begin
Print 'Error: Invalid Code'
Return 1
End
/*
If we got here then the parameter is validated
*/
PRINT '#OptParam3 is valid:'
PRINT #OptParam3
Return 0
You can check this by doing the following
/* Driver to test the stored procedure */
DECLARE #return_Code int
EXEC #return_Code = sp_demo 'parameterA','parameterB','A-1213-465-798-01';
PRINT #return_Code
But really you should probably be raising an error in your code if your input parameters are invalid. This will come through as a trapable error in your front end
IF OBJECT_ID('sp_demo') > 0 DROP PROC sp_demo
go
CREATE PROC sp_demo (#OptParam1 varchar(50),#OptParam2 varchar(50),#OptParam3 varchar(50))
AS
IF #OptParam3 not like '_-____-___-___-__'
Begin
RAISERROR (N'Error: Invalid Code "%s"',16,1 ,#OptParam3)
return ##ERROR
End
/*
If we got here then the parameter is validated
*/
PRINT '#OptParam3 is valid:'
PRINT #OptParam3
Return 0

Perl ADO thinks printed output in stored procedure is an error!

First of all (in case this is important) I'm using ActiveState's Perl (v5.8.7 built for MSWin32-x86-multi-thread).
I've just emerged from a three hour long debugging session, trying to find the source of an error. I found there was simply no error, but for some reason ADO's connection object was getting the Errors.Count increased with each printed message in my stored procedure's output.
Consider following Transact SQL code:
CREATE PROCEDURE dbo.My_Sample() AS
BEGIN TRAN my_tran
-- Does something useful
if ##error <> 0 BEGIN
ROLLBACK TRAN my_tran
RAISERROR( 'SP My_Sample failed', 16, 1)
END ELSE BEGIN
COMMIT TRAN my_tran
PRINT 'SP My_Sample succeeded'
END
Now imagine a Perl sub more or less like:
sub execute_SQL {
# $conn is an already opened ADO connection object
# pointing to my SQL Server
# $sql is the T-SQL statement to be executed
my($conn, $sql) = #_;
$conn->Execute($sql);
my $error_collection = $conn->Errors();
my $ecount = $error_collection->Count;
if ($ecount == 0 ) { return 0; }
print "\n" . $ecount . " errors found\n";
print "Executed SQL Code:\n$sql\n\n";
print "Errors while executing:\n";
foreach my $error (in $error_collection){
print "Error: [" . $error->{Number} . "] " . $error->{Description} . "\n";
}
return 1;
}
Somewhere else, in the main Perl code, I'm calling the above sub as:
execute_SQL( $conn, 'EXEC dbo.My_Sample' );
In the end I got it that every PRINT statement causes a new pseudo-error to be appended to the ADO Errors collection. The quick fix I implemented was to change that PRINT in the SP into a SELECT, to bypass this.
The questions I'd like to ask are:
Is this behaviour normal?
Is there a way to avoid/bypass it?
This is to be expected as it's what ADO does and the Win32::ADO is quite a thin layer above it.
ref: knowledge base note that the RAISERROR and PRINT statements are returned through the ADO errors collection
OK, after a lot of testing and reading, I came to found it explained in the BOLs' article "Using PRINT" (my emphasis):
The PRINT statement is used to return messages to applications. PRINT takes either a character or Unicode string expression as a parameter and returns the string as a message to the application. The message is returned as an informational error to applications using the SQLClient namespace or the ActiveX Data Objects (ADO), OLE DB, and Open Database Connectivity (ODBC) application programming interfaces (APIs). SQLSTATE is set to 01000, the native error is set to 0, and the error message string is set to the character string specified in the PRINT statement. The string is returned to the message handler callback function in DB-Library applications.
Armed with this knowledge I adapted this VB6 from this DevX article until I got this:
sub execute_SQL {
# $conn is an already opened ADO connection object
# pointing to my SQL Server
# $sql is the T-SQL statement to be executed
# Returns 0 if no error found, 1 otherwise
my($conn, $sql) = #_;
$conn->Execute($sql);
my $error_collection = $conn->Errors();
my $ecount = $error_collection->Count;
if ($ecount == 0 ) { return 0; }
my ($is_message, $real_error_found);
foreach my $error (in $error_collection){
$is_message = ($error->{SQLState} eq "01000" && $error->{NativeError}==0);
$real_error_found=1 unless $is_message;
if( $is_message) {
print "Message # " . $error->{Number}
. "\n Text: " . $error->{Description} ."\n";
} else {
print "Error # " . $error->{Number}
. "\n Description: " . $error->{Description}
. "\nSource: " . $error->{Source} . "\n";
}
}
print $message_to_print;
return $real_error_found;
}
So now my Perl sub correctly sorts out real errors (emitted from SQL Server via a RaisError) and a common message outputted via "PRINT".
Thanks to Richard Harrison for his answer which lead me to the way of success.

Resources