La sentencia CREATE FUNCTION define una función o un procedimiento que se puede llamar.
También puede utilizar la sentencia CREATE PROCEDURE para definir una función o un procedimiento que se puede llamar, que también se conoce como rutina.
>>-CREATE--| TipoRutina |--NombreRutina-------------------------> >--(--| ListaParámetros |--)--+-----------------+---------------> '-| TipoRetorno |-' >--+--------------+--+------------------------+-----------------> '-| Lenguaje |-' '-| ConjuntoResultados |-' >--| CuerpoRutina |-------------------------------------------->< TipoRutina |--+-FUNCTION--+------------------------------------------------| '-PROCEDURE-' ListaParámetros .-,-----------------. V | |----+---------------+-+----------------------------------------| '-| Parámetro |-' Parámetro (1) |--+-IN-----+--NombreParámetro--+-+----------+--TipoDatos-+-----> +-OUT----+ | '-CONSTANT-' | '-INOUT--' | (2) | +-NAMESPACE---------------+ '-NAME--------------------' .-NULLABLE-. >--+----------+-------------------------------------------------| '-NOT NULL-' TipoRetorno .-NULLABLE-. |--RETURNS--TipoDatos--+----------+-----------------------------| '-NOT NULL-' Lenguaje |--LANGUAGE--+-ESQL---------+-----------------------------------| | (3) | +-DATABASE-----+ +-.NET---------+ +-CLR----------+ '-JAVA---------' ConjuntoResultados |--DYNAMIC RESULT SETS--entero----------------------------------| CuerpoRutina |--+-Sentencia-------------------------------------------------------+--| '-EXTERNAL--NAME--NombreRutinaExterna--+------------------------+-' +-.NetTypeInfo-----------+ '-JavaClassLoaderService-' .NetTypeInfo |--ASSEMBLY--NombreEnsamblado-----------------------------------> .-------------------------------------------. V (4) | >--------+-----------------------------------+-+----------------| +-APPDOMAIN--NombreDominio----------+ +-VERSION--Versión------------------+ +-CULTURE--ContextoCultural---------+ '-PUBLICKEYTOKEN--SeñalClavePública-' JavaClassLoaderService |--CLASSLOADER--NombreServicioConfigurableCargadorClases--------|
Las sentencias CREATE FUNCTION y CREATE PROCEDURE definen una función o procedimiento que se pueden llamar, también denominadas rutinas.
En versiones anteriores de este producto, CREATE FUNCTION y CREATE PROCEDURE tenían distintas usos y posibilidades. Las mejoras posteriores han dado como resultado diferencias listadas anteriormente en las notas 1 y 3.
Las rutinas son útiles para crear bloques de código reutilizables que se pueden ejecutar de forma independiente muchas veces. Puede implementarlas como una serie de sentencias ESQL, un método Java™, un método .NET o un procedimiento almacenado de base de datos. Esta flexibilidad significa que algunas de las cláusulas del diagrama de sintaxis no son aplicables (o no están permitidas) para todos los tipos de rutina.
Cada rutina tiene un nombre, que debe ser exclusivo dentro del esquema al que pertenece. Por tanto, no se pueden sobrecargar los nombres de las rutinas; si el intermediario detecta que se ha sobrecargado un nombre de rutina, genera una excepción.
Especifique el nombre de la rutina utilizando la cláusula NombreRutina y los parámetros utilizando la cláusula ListaParámetros. Si la cláusula LANGUAGE especifica ESQL, implemente la rutina utilizando una única sentencia ESQL. Esta sentencia es muy útil si es una sentencia compuesta (BEGIN ... END), ya que entonces puede contener tantas sentencias ESQL como sea necesario para desempeñar su función.
En lugar de proporcionar un cuerpo ESQL para la rutina, también puede especificar una cláusula LANGUAGE distinta de ESQL. Esto le permite utilizar la cláusula EXTERNAL NAME para proporcionar una referencia al cuerpo real de la rutina, dondequiera que se ubique fuera del intermediario. Para obtener más información sobre cómo utilizar la cláusula EXTERNAL NAME, consulte Invocar procedimientos almacenados y Llamada a una rutina Java.
Las rutinas con LANGUAGE de cualquier tipo pueden tener parámetros IN, OUT e INOUT. El canal de llamada puede pasar varios valores a la rutina y recibir de vuelta varios valores actualizados. Estos parámetros devueltos se suman a las cláusulas RETURNS que haya definido para la rutina. La cláusula RETURNS define el valor que la rutina devuelve el canal de llamada.
Las rutinas que están implementadas en distintos lenguajes tienen sus propias restricciones sobre qué tipos de datos se pueden pasar o devolver; estas restricciones se indican más adelante en eta sección. El tipo de datos del valor devuelto debe coincidir con el tipo de datos del valor definido para devolverse desde la rutina. Además, si una rutina está definida para que tenga un valor de retorno, el canal de llamada de la rutina no puede ignorarlo. Para obtener más información, consulte Sentencia CALL.
Para cualquier lenguaje o tipo de rutina, el método de invocación de la rutina debe coincidir con la forma de declaración de la rutina. Si la rutina tiene una cláusula RETURNS, utilice la sintaxis de invocación FUNCTION o una sentencia CALL con una cláusula INTO. Y a la inversa; si una rutina no tiene ninguna cláusula RETURNS, debe utilizar una sentencia CALL sin una cláusula INTO.
Si el tipo de rutina es FUNCTION, el indicador de dirección (IN, OUT, INOUT) es opcional para cada parámetro. Sin embargo, en programación se recomienda especificar un indicador de dirección para todas las nuevas rutinas de cualquier tipo.
Las variables ESQL que se declaran como CONSTANT (o las referencias a las variables declaradas como CONSTANT) no pueden tener la dirección OUT o INOUT.
Las rutinas ESQL están escritas en ESQL y tienen una cláusula LANGUAGE de ESQL. El cuerpo de una rutina ESQL normalmente es una sentencia compuesta de la forma BEGIN… END, que contiene múltiples sentencias para procesar los parámetros que han pasado a la rutina.
CREATE PROCEDURE swapParms (
IN parm1 CHARACTER,
OUT parm2 CHARACTER,
INOUT parm3 CHARACTER )
BEGIN
SET parm2 = parm3;
SET parm3 = parm1;
END;
Este procedimiento de ejemplo muestra el uso repetitivo de una rutina ESQL. Analiza un árbol, visitando todos los lugares desde un punto de partida concreto, y genera informes sobre lo que ha encontrado:
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;
Cuando se proporciona el siguiente mensaje de entrada:
<Person>
<Name>John Smith</Name>
<Salary period='monthly' taxable='yes'>-1200</Salary>
</Person>
el procedimiento genera la salida siguiente que se ha formateado manualmente:
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
Una rutina .NET se implementa como un método .NET y tiene una cláusula LANGUAGE de .NET o CLR. Para rutinas .NET, el NombreRutinaExterna debe contener el nombre de clase y el nombre de método del método .NET que se va a invocar. Especifique el NombreRutinaExterna como en este ejemplo:
>>--"-- nombreClase---.---nombreMétodo--"--------------><
Donde
nombreClase identifica la clase que contiene el método y
nombreMétodo identifica el método a invocar. Si la clase forma parte
de un Espacio de nombres o es una clase anidad, la parte del identificador de clase debe
incluir todos los nombres de Espacio de nombres y clase anidada; por ejemplo,
"IBM.Broker.test.MyOuterClass.MyNestedClass.MyMethod"Para encontrar la clase .NET, el intermediario busca la GAC y la ubicación base DominioAplic del ensamblado especificado.
Cualquier método .NET que desee invocar debe ser un método estático público. Además, todos los parámetros deben aparecer listados en Tablas de correlación de tipo de datos de ESQL a .NET. Asimismo, si el método tiene un tipo de retorno, el tipo de retorno debe aparecer listado en la tabla de correlaciones de tipos de datos IN.
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;
-- o
SET ReturnVar = Swap ( intVar1, intVar2, intVar3);
Define un procedimiento que representa un Método .NET que no devuelve ningún valor con tres parámetros anulables de direcciones variadas.
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;
}
}
>>--"-- nombreClase---.---nombreMétodo--"--------------><
donde
nombreClase identifica la clase que contiene el método
y nombreMétodo identifica el método a invocar. Si la
clase forma parte de un paquete, la parte correspondiente al identificador
de clase debe incluir el prefijo del paquete completo; por ejemplo,
"com.ibm.broker.test.MyClass.myMethod". Para encontrar la clase Java, el intermediario utiliza el método de búsqueda que se describe en Despliegue de clases Java.
public static <return-type> <method-name> (<parámetros 0 - N>)
Donde <tipo-retorno> debe estar en la lista de tipos de datos IN de Java de la tabla de Correlación de tipo de datos ESQL a Java (excluyendo el tipo REFERENCE, que no está permitido como valor de retorno) o el tipo de datos void (vacío) de Java. Los tipos de datos de parámetros también deben estar en la tabla Correlación de tipo de datos ESQL a Java. Además, no se permite que el método Java tenga la cláusula exception throws en su firma.
La cláusula de la sección JavaClassLoader sólo se aplica a las rutinas LANGUAGE JAVA. La cláusula CLASSLOADER es opcional; si no especifica esta cláusula, el cargador de clases EGShared carga la clase Java. Para obtener más información, consulte Carga de clases del nodo JavaCompute y Servicio configurable JavaClassLoader.
Puede utilizar la API de nodo Java definido por el usuario en el método Java, si cumple las restricciones documentadas en Restricciones en rutinas Java. Para obtener más información sobre cómo utilizar la API Java, consulte Compilación de un nodo Java definido por el usuario.
Esta rutina contiene tres parámetros de diversas direcciones y devuelve un entero, que se correlaciona a un tipo de retorno Java de 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";
Puede utilizar el siguiente procedimiento ESQL para invocar amyProc1:
CALL myProc1( intVar1, intVar2, intVar3) INTO intReturnVar3;
-- o
SET intReturnVar3 = myProc1( intVar1, intVar2, intVar3);
Esta rutina contiene tres parámetros de diversas direcciones y tiene un tipo de retorno Java de void.
CREATE PROCEDURE myProc2( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER )
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod2";
Debe utilizar el siguiente procedimiento ESQL para invocar myProc2:
CALL myProc2(intVar1, intVar2, intVar3);
La siguiente clase Java proporciona un método para cada uno de los ejemplos Java anteriores:
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) { ... }
/* Cuando se llama a cualquiera de estos métodos:
P1 puede ser o no NULL (dependiendo del valor de intVar1).
P2[0] siempre es NULL (sea cual sea el valor de intVar2).
P3[0] puede ser o no NULL (dependiendo del valor de intVar3).
Es lo mismo que con las rutinas LANGUAGE ESQL.
Estos métodos pueden devolver:
intVar1 sin modificar
intVar2 modificado o todavía con el valor NULL
intVar3 modificado o todavía con el mismo valor
Es lo mismo que con las rutinas LANGUAGE ESQL.
Cuando myMethod1 devuelve: intReturnVar3 puede ser NULL (si
el método devuelve NULL) o contiene el valor devuelto por el
método.
*/
}
CREATE FUNCTION myMethod1 ( IN P1 INTEGER, IN P2 INTEGER )
RETURNS INTEGER
LANGUAGE JAVA
EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod1"
CLASSLOADER "myClassLoader";
Tipos de datos ESQL 1 | Tipos de datos Java IN | Tipos de datos Java INOUT y OUT |
---|---|---|
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 | No soportado | No soportado |
BOOLEAN | java.lang.Boolean | java.lang.Boolean[] |
REFERENCE (a un árbol de mensaje) 3 4 5 6 | com.ibm.broker.plugin.MbElement | com.ibm.broker.plugin.MbElement[] (Soportado para INOUT. No soportado para OUT) |
ROW | No soportado | No soportado |
LIST | No soportado | No soportado |
Por ejemplo, si se pasa una referencia ESQL a OutputRoot.XML.Test a un método Java como un MbElement INOUT, pero se vuelve a pasar un MbElement diferente a ESQL cuando se devuelve la llamada, el elemento diferente también debe apuntar a algún lugar del árbol OutputRoot.
Puede utilizarse una referencia (REFERENCE) a una variable escalar en la llamada (CALL) de un método Java, siempre que el tipo de datos de la variable a la que REFERENCE hace referencia coincida con el tipo de datos correspondiente en la signatura del programa Java.
Puede crear hebras dentro del método. Sin embargo, las hebras creados no deben utilizar las API de Java y se debe devolver el control al intermediario.
Todas las restricciones que se aplican al uso de la API de Java también se aplican a los métodos API Java llamados desde ESQL.
El método más eficiente y flexible de desplegar en el intermediario consiste en añadir el archivo JAR al archivo BAR. Puede realizar esta acción manualmente o automáticamente utilizando WebSphere Message Broker Toolkit.
Si WebSphere Message Broker Toolkit encuentra la clase Java correcta en un proyecto Java al que se hace referencia abierto en el espacio de trabajo, compila automáticamente la clase Java en un archivo JAR y lo añade al archivo BAR. Este procedimiento es el mismo que se sigue para desplegar un nodo JavaCompute en un JAR, tal como se describe en Carga de clases de nodos definidos por el usuario.
Al desplegar un archivo JAR desde WebSphere Message Broker Toolkit, el flujo que se ha vuelto a desplegar vuelve a cargar el archivo JAR contenido en el archivo BAR.
Los archivos también se vuelven a cargar si se detiene y se reinicia el flujo de mensajes que hace referencia a una clase Java. Asegúrese de detener y reiniciar (o volver a desplegar) todos los flujos que hacen referencia al archivo JAR que desea actualizar. Esta acción evita el problema de algunos flujos ejecutándose con la versión antigua del archivo JAR y otros flujos ejecutándose con la versión nueva.
WebSphere Message Broker Toolkit sólo despliega archivos JAR; no despliega archivos de clase Java autónomos.
Debe completar esta acción manualmente; no puede utilizar WebSphere Message Broker Toolkit.
En este método, al desplegar de nuevo el flujo de mensajes no se vuelven a cargar las clases Java a las que se hace referencia; éstas tampoco se vuelven a cargar al detener y reiniciar el flujo de mensajes. En este caso, la única manera de volver a cargar las clases es detener y reiniciar el intermediario mismo.
Para permitir que el intermediario encuentre una clase Java, asegúrese de que esté en una de las ubicaciones anteriores. Si el intermediario no puede encontrar la clase especificada, genera una excepción.
Aunque dispone de las opciones mostradas anteriormente cuando desplegó el archivo JAR, si utiliza WebSphere Message Broker Toolkit para desplegar el archivo BAR obtendrá la máxima flexibilidad cuando vuelva a desplegar el archivo JAR.
CREATE FUNCTION no da soporte a las rutinas de base de datos. Utilice CREATE PROCEDURE para definir una rutina de base de datos.