How to Use Output Arguments with Stored Procedures in Xbasic

Description

Most queries performed on a SQL database use Input arguments. However, Xbasic offers the ability to also use Output and InputOutput arguments return values from stored procedures.

Discussion

When you use execute SQL statements from Xbasic using AlphaDAO it is very common to use the SQL::Arguments object to set values in SQL query statement. For example:

dim args as sql::arguments
args.add("customerId","alfki")
dim cn as sql::connection
dim flag as l
if (cn.open("::Name::Northwind")) then
    dim sql as c
    sql  = "Select * from customers where customerId = :customerId"
    if (cn.execute(sql,args)) then
        '...
    end if
end if

In the above example, the argument (customerId) is an 'input' argument - it is passing a value in to the database engine.

In addition to 'input' arguments, you can also create 'output' arguments and 'inputOutput' arguments. The 'output' and 'inputOutput' argument types can be used to pass data out of the database to the Xbasic script executing the query.

For example, consider the following (very simplistic) stored procedure for SQL Server:

CREATE PROCEDURE [dbo].[output]
-- Add the parameters for the stored procedure here
@Param1 integer output,
@Param2 varchar(30) output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
set @Param1 = 100
set @Param2 = 'Hello world'
return
END

In this stored procedure, the parameters Param1 and Param2 are set to 100 and 'Hello World' respectively.

To call the stored procedure from Xbasic and get the value of Param1 and Param2 , inputOutput SQL::Arguments are created and passed in as part of the Xbasic query:

dim args as sql::arguments

'define to arguments, 'inout' and 'inout2' as inputOutput arguments
args.add("inout",0,sql::ArgumentUsage::inputoutputargument)

'notice that a dummy value of the correct size needs to be defined for the argument
args.add("inout2",replicate(" ",40),sql::ArgumentUsage::inputoutputargument)

dim cn as sql::Connection
cn.open("::Name::sqlserver_northwind")

'call the stored procedure and pass in the two argument values
? cn.Execute("exec output :inout, :inout2",args)
= .T.

? args[1].data
= 100

? args[2].data
= "Hello world"

Note that the value of the second input argument, inout2, is set to a character string containing 40 spaces by calling replicate(" ",40). This is done so the size of the inout2 parameter is large enough to hold the result of the stored procedure. If the size of the argument is not large enough to hold the result from the stored procedure, it can result in an error. For example:

? cn.CallResult.text
= "Internal Error - Data Truncated - The buffer for receiving data is too short"

In some cases, a stored procedure will also return one or more result sets in addition to output arguments.

Depending on the type database being using, you may have to process all of the result sets returned before the output arguments can be read.

For example, consider the following stored procedure:

CREATE PROCEDURE [dbo].[output]
-- Add the parameters for the stored procedure here
@Param1 integer output,
@Param2 varchar(30) output
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
set @Param1 = 100
set @Param2 = 'Hello world'
select * from customers
select * from orders
return
END

This stored procedure now returns two result sets: select * from customers and select * from orders. Before the values of the two parameters can be accessed, the results from both of these SQL queries must be processed:

dim args as sql::arguments
args.add("inout",0,sql::ArgumentUsage::inputoutputargument)
args.add("inout2",replicate(" ",50),sql::ArgumentUsage::inputoutputargument)
dim cn as sql::Connection
cn.open("::Name::sqlserver_northwind")
?cn.Execute("exec output :inout, :inout2",args)
= .T.

'value of the output arguments not yet available because we have
'not read the result sets
? args[1].data
= 0
? args[2].data
= ""

dim rs as sql::ResultSet
rs = cn.ResultSet

'still not available
? args[1].data
= 0
? args[2].data
= ""

'get the next result set
? rs.NextResult()
= .T.

'still not available
?args[1].data
= 0
? args[2].data
= ""

'no more result sets to read
? rs.NextResult()
= .F.

'argument value are now available!!
? args[1].data
= 100

? args[2].data
= "Hello world"

To learn more, watch the video below.

Using Output Arguments when Calling a Stored Procedure

When a stored procedure is executed from Xbasic, the procedure may return values in addition to result sets for SELECT queries. Xbasic 'output' arguments can be passed to the stored procedure to capture these values.

In this video, we show how input/output and output arguments are defined and used when calling a stored procedure.

2016-03-18