The CREATE PROCEDURE statement defines a callable function or procedure.
You can also use the CREATE FUNCTION statement to define a callable function or procedure, also known as a routine.
Each routine has a name, which must be unique within the schema to which it belongs. Routine names therefore cannot be overloaded; if the broker detects that a routine name has been overloaded, it raises an exception.
>>-CREATE--| RoutineType |--RoutineName-------------------------> >--(--| ParameterList |--)--+----------------+------------------> '-| ReturnType |-' >--+--------------+--+---------------+--| RoutineBody |-------->< '-| Language |-' '-| ResultSet |-' RoutineType |--+-FUNCTION--+------------------------------------------------| '-PROCEDURE-' ParameterList .-,-----------------. V | |----+---------------+-+----------------------------------------| '-| Parameter |-' Parameter (1) |--+-IN-----+--ParameterName--+-+----------+--DataType-+--------> +-OUT----+ | '-CONSTANT-' | '-INOUT--' | (2) | +-NAMESPACE--------------+ '-NAME-------------------' .-NULLABLE-. >--+----------+-------------------------------------------------| '-NOT NULL-' ReturnType .-NULLABLE-. |--RETURNS--DataType--+----------+------------------------------| '-NOT NULL-' Language |--LANGUAGE--+-ESQL---------+-----------------------------------| | (3) | +-DATABASE-----+ +-.NET---------+ +-CLR----------+ '-JAVA---------' ResultSet |--DYNAMIC RESULT SETS--integer---------------------------------| RoutineBody |--+-Statement-------------------------------------------------------+--| '-EXTERNAL--NAME--ExternalRoutineName--+------------------------+-' +-.NetTypeInfo-----------+ '-JavaClassLoaderService-' .NetTypeInfo |--ASSEMBLY--AssemblyName---------------------------------------> .----------------------------------------. V (4) | >--------+--------------------------------+-+-------------------| +-APPDOMAIN--DomainName----------+ +-VERSION--Version---------------+ +-CULTURE--Culture---------------+ '-PUBLICKEYTOKEN--PublicKeyToken-' JavaClassLoaderService |--CLASSLOADER--ClassLoaderConfigurableServiceName--------------|
If the routine type is FUNCTION, the direction indicator (IN, OUT, INOUT) is optional for each parameter. However, it is good programming practice to specify a direction indicator for all new routines of any type for documentation purposes.
ESQL variables that are declared to be CONSTANT (or references to variables declared to be CONSTANT) are not allowed to have the direction OUT or INOUT.
ESQL routines are written in ESQL, and have a LANGUAGE clause of ESQL. The body of an ESQL routine is typically a compound statement of the form BEGIN … END, that contains multiple statements for processing the parameters that are passed to the routine.
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
OUT parm2 CHARACTER,
INOUT parm3 CHARACTER )
BEGIN
SET parm2 = parm3;
SET parm3 = parm1;
END;
This example procedure shows the recursive use of an ESQL routine. It parses a tree, visiting all places at and below the specified starting point, and reports what it has found:
SET OutputRoot.MQMD = InputRoot.MQMD;
DECLARE answer CHARACTER;
SET answer = '';
CALL navigate(InputRoot.XMLNS, answer);
SET OutputRoot.XMLNS.Data.FieldNames = answer;
CREATE PROCEDURE navigate (IN root REFERENCE, INOUT answer CHARACTER)
BEGIN
SET answer = answer || 'Reached Field... Type:'
|| CAST(FIELDTYPE(root) AS CHAR)||
': Name:' || FIELDNAME(root) || ': Value :' || root || ': ';
DECLARE cursor REFERENCE TO root;
MOVE cursor FIRSTCHILD;
IF LASTMOVE(cursor) THEN
SET answer = answer || 'Field has children... drilling down ';
ELSE
SET answer = answer || 'Listing siblings... ';
END IF;
WHILE LASTMOVE(cursor) DO
CALL navigate(cursor, answer);
MOVE cursor NEXTSIBLING;
END WHILE;
SET answer = answer || 'Finished siblings... Popping up ';
END;
When given the following input message:
<Person>
<Name>John Smith</Name>
<Salary period='monthly' taxable='yes'>-1200</Salary>
</Person>
the procedure produces the following output, which has been manually formatted:
Reached Field... Type:16777232: Name:XML: Value :: Field has children...
drilling down
Reached Field... Type:16777216: Name:Person: Value :: Field has children...
drilling down
Reached Field... Type:16777216: Name:Name:
Value :John Smith: Field has children... drilling down
Reached Field... Type:33554432: Name::
Value :John Smith: Listing siblings... Finished siblings... Popping up
Finished siblings... Popping up
Reached Field... Type:16777216: Name:Salary:
Value :-1200: Field has children... drilling down
Reached Field... Type:50331648: Name:period:
Value :monthly: Listing siblings... Finished siblings... Popping up
Reached Field... Type:50331648: Name:taxable:
Value :yes: Listing siblings... Finished siblings... Popping up
Reached Field... Type:33554432: Name::
Value :-1200: Listing siblings... Finished siblings... Popping up
Finished siblings... Popping up
Finished siblings... Popping up
Finished siblings... Popping up
A .NET routine is implemented as a .NET method, and has a LANGUAGE clause of .NET or CLR. For .NET routines, the ExternalRoutineName must contain the class name and method name of the .NET method to be called. Specify the ExternalRoutineName like this example:
>>--"-- className---.---methodName--"--------------><
Where className identifies
the class that contains the method and methodName identifies
the method to invoke. If the class is part of a Namespace or is a
nested class, the class identifier part must include all Namespace
and nested class names; for example,"IBM.Broker.test.MyOuterClass.MyNestedClass.MyMethod"To find the .NET class, the broker searches the GAC and the AppDomain base location for the specified assembly.
Any .NET method that you want to invoke must be a public static method. In addition, all parameters must be listed in ESQL-to-.NET data-type mapping tables. Also, if the method has a return type, the return type must be listed in the IN data type mapping table.
CREATE PROCEDURE Swap (
IN a INT NOT NULL,
OUT b INT NOT NULL,
INOUT c INT NOT NULL ) RETURNS CHARACTER NOT NULL
LANGUAGE .NET
EXTERNAL NAME "FunctionTests.SwapString"
ASSEMBLY "C:\coding\test projects\MyAssembly"
APPDOMAIN "MyDomain";
CALL Swap( intVar1, intVar2, intVar3 ) INTO ReturnVar;
-- or
SET ReturnVar = Swap ( intVar1, intVar2, intVar3);
Defines a procedure representing a .NET Method that has no return value with three Nullable parameters of varying directions.
CREATE PROCEDURE SwapNullable (
IN a INTEGER NULLABLE,
OUT b INTEGER NULLABLE,
INOUT c INTEGER NULLABLE )
LANGUAGE CLR
EXTERNAL NAME "FunctionTests.SwapStringNullable"
ASSEMBLY "MyAssembly2"
APPDOMAIN "MyDomain";
CALL SwapNullable(intVar1, intVar2, intVar3);
C#
public class FunctionTests
{
public static string Swap(int pIn, out int pOut, ref int pInout)
{
pOut = pInout;
pInout = pIn;
return "Finished";
}
public static void SwapNullable(long? pIn, out long? pOut, ref long? pInout)
{
pOut = pInout;
pInout = pIn;
}
}
VB
Public Class FunctionTests
Shared Function Swap(ByVal pIn As Integer, <Out()> ByRef pOut As Integer, ByRef pInout As Integer) As String
pOut = pInout
pInout = pIn
Return "Finished"
End Function
Shared Sub SwapNullable(ByVal pIn As Long?, ByRef pOut As Long?, ByRef pInout As Long?)
pOut = pInout
pInout = pIn
End Sub
End Class
F#
module FunctionTests
let Swap( pIn : int, [<Out>] pOut : byref<int> , pInOut : byref<int> ) = (
pOut <- pInout
pInout <- pIn
let temp = "Finished"
temp
)
let SwapNullable( pIn : Nullable<int64>, [<Out>] pOut : byref<Nullable<int64>> , pInOut : byref<Nullable<int64>> ) = (
pOut <- pInout
pInout)
)
C++ / CLi
public ref class FunctionTests
{
public:
static String^ Swap(int pIn, [Out] int% pOut, int% pInout)
{
pOut = pInout;
pInout = pIn;
String^ temp = "Finished";
return temp;
}
static void SwapNullable(Nullable<long long> pIn, [Out] Nullable<long long>% pOut, Nullable<long long>% pInout)
{
pOut = pInout;
pInout = pIn;
}
}
>>--"-- className---.---methodName--"--------------><
where className identifies
the class that contains the method and methodName identifies
the method to invoke. If the class is part of a package, the class
identifier part must include the complete package prefix; for example,"com.ibm.broker.test.MyClass.myMethod" To find the Java class, the broker uses the search method that is described in Deploying Java classes.
public static <return-type> <method-name> (< 0 - N parameters>)
Where <return-type> must be in the list of Java IN data types in the table in ESQL to Java data type mapping (excluding the REFERENCE type, which is not permitted as a return value), or the Java void data type. The parameter data types must also be in the ESQL to Java data type mapping table. In addition, the Java method is not allowed to have exception throws clause in its signature.
The clause in the JavaClassLoader section applies only to LANGUAGE JAVA routines. The CLASSLOADER clause is optional; if you do not specify this clause, the Java class is loaded by the EGShared classloader. For more information, see JavaCompute node classloading and JavaClassLoader configurable service.
You can use the Java user-defined node API in your Java method, if you observe the restrictions documented in Restrictions on Java routines. For more information about using the Java API, see Compiling a Java user-defined node.
This routine contains three parameters of varying directions, and returns an integer, which maps to a Java return type of java.lang.Long.
CREATE FUNCTION myProc1( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
RETURNS INTEGER
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1";
You can use the following ESQL to invoke myProc1:
CALL myProc1( intVar1, intVar2, intVar3) INTO intReturnVar3;
-- or
SET intReturnVar3 = myProc1( intVar1, intVar2, intVar3);
This routine contains three parameters of varying directions and has a Java return type of void.
CREATE PROCEDURE myProc2( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod2";
You must use the following ESQL to invoke myProc2:
CALL myProc2(intVar1, intVar2, intVar3);
The following Java class provides a method for each of the preceding Java examples:
package com.ibm.broker.test;
class MyClass {
public static Long myMethod1( Long P1, Long[] P2 Long[] P3) { ... }
public static void myMethod2( Long P2, Long[] P2 Long[] P3) { ... }
/* When either of these methods is called:
P1 might or might not be NULL (depending on the value of intVar1).
P2[0] is always NULL (whatever the value of intVar2).
P3[0] might or might not be NULL (depending on the value of intVar3).
This is the same as with LANGUAGE ESQL routines.
When these methods return:
intVar1 is unchanged
intVar2 might still be NULL or might have been changed
intVar3 might contain the same value or might have been changed.
This is the same as with LANGUAGE ESQL routines.
When myMethod1 returns: intReturnVar3 is either NULL (if the
method returns NULL) or it contains the value returned by the
method.
*/
}
CREATE FUNCTION myMethod1 ( IN P1 INTEGER, IN P2 INTEGER )
RETURNS INTEGER
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1"
CLASSLOADER "myClassLoader";
ESQL data types 1 | Java IN data types | Java INOUT and OUT data types |
---|---|---|
INTEGER, INT | java.lang.Long | java.lang.Long [] |
FLOAT | java.lang.Double | java.lang.Double[] |
DECIMAL | java.math.BigDecimal | java.math.BigDecimal[] |
CHARACTER, CHAR | java.lang.String | java.lang.String[] |
BLOB | byte[] | byte[][] |
BIT | java.util.BitSet | java.util.BitSet[] |
DATE | com.ibm.broker.plugin.MbDate | com.ibm.broker.plugin.MbDate[] |
TIME 2 | com.ibm.broker.plugin.MbTime | com.ibm.broker.plugin.MbTime[] |
GMTTIME 2 | com.ibm.broker.plugin.MbTime | com.ibm.broker.plugin.MbTime[] |
TIMESTAMP 2 | com.ibm.broker.plugin.MbTimestamp | com.ibm.broker.plugin.MbTimestamp[] |
GMTTIMESTAMP 2 | com.ibm.broker.plugin.MbTimestamp | com.ibm.broker.plugin.MbTimestamp[] |
INTERVAL | Not supported | Not supported |
BOOLEAN | java.lang.Boolean | java.lang.Boolean[] |
REFERENCE (to a message tree) 3 4 5 6 | com.ibm.broker.plugin.MbElement | com.ibm.broker.plugin.MbElement[] (Supported for INOUT. Not supported for OUT) |
ROW | Not supported | Not supported |
LIST | Not supported | Not supported |
For example, if an ESQL reference to OutputRoot.XML.Test is passed into a Java method as an INOUT MbElement, but a different MbElement is passed back to ESQL when the call returns, the different element must also point to somewhere in the OutputRoot tree.
A REFERENCE to a scalar variable can be used in the CALL of a Java method, provided that the data type of the variable to which the reference refers matches the corresponding data type in the Java program signature.
You can create threads inside your method. However, created threads must not use the Java APIs, and you must return control back to the broker.
All restrictions that apply to the usage of the Java API also apply to Java methods that are called from ESQL.
The most efficient and flexible method of deploying to the broker is to add your JAR file to the BAR file. You can do this manually or automatically using the IBM® Integration Toolkit.
If the IBM Integration Toolkit finds the correct Java class inside a referenced Java project open in the workspace, it automatically compiles the Java class into a JAR file and adds it to the BAR file. This procedure is the same procedure that you follow to deploy a JavaCompute node inside a JAR, as described in User-defined node class loading.
When you deploy a JAR file from the IBM Integration Toolkit, the flow that has been redeployed reloads the JAR file contained in the BAR file.
The files are also reloaded if the message flow that references a Java class is stopped and restarted. There is no need to stop and restart flows or redeploy them, because the ESQL manager is refreshed when JAR file is redeployed and any subsequent external Java calls from ESQL use the new classloader. After the deploy has finished, all flows are running with the new version of the JAR file.
The IBM Integration Toolkit deploys only JAR files; it does not deploy stand-alone Java class files.
You must complete this action manually; you cannot use the IBM Integration Toolkit.
In this method, redeploying the message flow does not reload the referenced Java classes; neither does stopping and restarting the message flow. The only way to reload the classes in this case is to stop and restart the broker itself.
To enable the broker to find a Java class, ensure that it is in one of the preceding locations. If the broker cannot find the specified class, it generates an exception.
Although you have the choices shown previously when you deploy the JAR file, by using the IBM Integration Toolkit to deploy the BAR file provides the greatest flexibility when redeploying the JAR file.
Database routines are implemented as database stored procedures. Database routines have a LANGUAGE clause of DATABASE, and must have a routine type of PROCEDURE.
When writing stored procedures in languages like C, you must use NULL indicators to ensure that your procedure can process the data correctly.
Although the database definitions of a stored procedure vary between the databases, the ESQL used to invoke them does not. The names given to parameters in the ESQL do not have to match the names they are given on the database side. However, the external name of the routine, including any package or container specifications, must match its defined name in the database.
The DYNAMIC RESULT SETS clause is allowed only for database routines. It is required only if a stored procedure returns one or more result sets. The integer parameter to this clause must be 0 (zero) or more, and specifies the number of result sets to be returned.
The optional RETURNS clause is required if a stored procedure returns a single scalar value.
EXTERNAL NAME "mySchema.myProc";
EXTERNAL NAME "mySchema.myPackage.myProc";
This form allows the schema, but not the package name, to be chosen dynamically in the CALL statement.
EXTERNAL NAME "mySchema.Proc_";
>>--"schemaName---.---packageName---.---procedureName--"--------------><
where:The following example shows an ESQL definition of a stored procedure that returns a single scalar value and an OUT parameter:
CREATE PROCEDURE myProc1(IN P1 INT, OUT P2 INT)
RETURNS INTEGER
LANGUAGE DATABASE
EXTERNAL NAME "myschema.myproc";
Use this ESQL to invoke the myProc1 routine:
/*using CALL statement invocation syntax*/
CALL myProc1(intVar1, intVar2) INTO intReturnVar3;
/*or using function invocation syntax*/
SET intReturnVar3 = myProc1(intVar1, intVar2);
The following ESQL code demonstrates how to define and call DB2 stored procedures:
ESQL Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;
SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
OUT parm2 CHARACTER,
INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;
To register this stored procedure with DB2, copy the following script to a file (for example, test1.sql)
-- DB2 Example Stored Procedure
DROP PROCEDURE dbSwapParms @
CREATE PROCEDURE dbSwapParms
( IN in_param CHAR(32),
OUT out_param CHAR(32),
INOUT inout_param CHAR(32))
LANGUAGE SQL
BEGIN
SET out_param = inout_param;
SET inout_param = in_param;
END @
Now run the file from the DB2 command
prompt:db2 -td@ -vf test1.sql
The following ESQL code demonstrates how to define and call Oracle stored procedures:
ESQL Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;
SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
OUT parm2 CHARACTER,
INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;
To register this stored procedure with Oracle, copy the following script to a file (for example, test1.sql)
CREATE OR REPLACE PROCEDURE dbSwapParms
( in_param IN VARCHAR2,
out_param OUT VARCHAR2,
inout_param IN OUT VARCHAR2 )
AS
BEGIN
out_param := inout_param;
inout_param := in_param;
END;
/
Now run the file:sqlplus userID/password @test1.sql
The following ESQL code demonstrates how to define and call SQL Server stored procedures:
ESQL Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;
SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
INOUT parm2 CHARACTER,
INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;
To register this stored procedure with SQL Server, copy the following script to a file (for example, test1.sql)
-- SQLServer Example Stored Procedure
DROP PROCEDURE dbSwapParms
go
CREATE PROCEDURE dbSwapParms
@in_param CHAR(32),
@out_param CHAR(32) OUT,
@inout_param CHAR(32) OUT
AS
SET NOCOUNT ON
SET @out_param = @inout_param
SET @inout_param = @in_param
go
Now run file:isql -UuserID -Ppassword -Sserver -ddatasource -itest1.sql
SQL Server considers OUTPUT parameters from stored procedures as INPUT/OUTPUT parameters. If you declare them as OUT parameters in your ESQL you encounter a type mismatch error at run time. To avoid that mismatch you must declare SQL Server OUTPUT parameters as INOUT in your ESQL.
The following ESQL code demonstrates how to define and call Sybase stored procedures:
ESQL Definition:
DECLARE inputParm CHARACTER;
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER;
SET inputParm = 'Hello';
SET inputOutputParm = 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
INOUT parm2 CHARACTER,
INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;
To register this stored procedure with Sybase, copy the following script to a file (for example, test1.sql)
-- SYBASE Example Stored Procedure
DROP PROCEDURE dbSwapParms
go
CREATE PROCEDURE dbSwapParms
@in_param CHAR(32),
@out_param CHAR(32) OUT,
@inout_param CHAR(32) OUT
AS
SET @out_param = @inout_param
SET @inout_param = @in_param
go
Now run file:isql -U<userID> -P<password> -S<server> -D<datasource> -itest1.sql
Sybase considers OUTPUT parameters from stored procedures as INPUT/OUTPUT parameters. If you declare them as OUT parameters in your ESQL, you encounter a type mismatch error at run time. To avoid that mismatch, declare Sybase OUTPUT parameters as INOUT in your ESQL.
The following ESQL code demonstrates how to define and call Informix® stored procedures:
ESQL Definition:
DECLARE inputParm CHARACTER 'Hello';
DECLARE outputParm CHARACTER;
DECLARE inputOutputParm CHARACTER 'World';
CALL swapParms( inputParm, outputParm, inputOutputParm );
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
INOUT parm2 CHARACTER,
INOUT parm3 CHARACTER
)
LANGUAGE DATABASE
EXTERNAL NAME dbSwapParms;
To register this stored procedure with Informix, copy the following script to a file (for example, test1.sql)
DROP SPECIFIC PROCEDURE dbSwapParms;
CREATE PROCEDURE dbSwapParms
( inParm CHAR(20),
OUT outParm CHAR(20),
INOUT inoutParm CHAR(20))
SPECIFIC dbSwapParms
LET outParm = inoutParm;
LET inoutParm = inParm;
END PROCEDURE;
Now run file:From the Informix server shell environment enter:
dbaccess <dataBaseName> <fully qualified path/test1.sql>
The following restrictions apply to Informix stored procedures:
This example shows how to call a stored procedure that returns two result sets, in addition to an out parameter:
CREATE PROCEDURE myProc1 (IN P1 INT, OUT P2 INT)
LANGUAGE DATABASE
DYNAMIC RESULT SETS 2
EXTERNAL NAME "myschema.myproc";
Use the following ESQL to invoke myProc1:
/* using a field reference */
CALL myProc1(intVar1, intVar2, Environment.RetVal[], OutputRoot.XMLNS.A[])
/* using a reference variable*/
CALL myProc1(intVar1, intVar2, myReferenceVariable.RetVal[], myRef2.B[])