Las sentencias CREATE FUNCTION y CREATE PROCEDURE definen una función o procedimiento que se pueden llamar, normalmente denominadas rutinas.
Las sentencias CREATE FUNCTION y CREATE PROCEDURE definen una función o procedimiento que se pueden llamar, normalmente denominadas rutinas.
Las rutinas son útiles para crear bloques de código reutilizables que se pueden ejecutar independientemente muchas veces. Pueden implementarse como una serie de sentencias ESQL, un método Java o un procedimiento almacenado en una base de datos. Esta flexibilidad significa que algunas de las cláusulas en el diagrama de sintaxis de no son aplicables (o permitidas) a todos los tipos de rutina.
Cada rutina tiene un nombre, que debe ser exclusivo dentro del esquema al que pertenece. Esto significa que no se pueden sobrecargar los nombre de las rutinas; si el intermediario detecta que se ha sobrecargado una rutina, genera una excepción.
Especifique el nombre de la rutina utilizando la cláusula NombreRutina y los parámetros de la rutina utilizando la cláusula ListaParámetros. Si la cláusula LANGUAGE especifica ESQL, la rutina debe implementarse utilizando una sola 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 cumplir su función.
De forma alternativa, en lugar de proporcionar un cuerpo ESQL para la rutina, 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 la utilización de 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. Esto permite al canal de llamada pasar varios valores a la rutina y recibir de vuelta varios valores actualizados. Esto es además de cualquier cláusula RETURNS que pueda tener la rutina. La cláusula RETURNS permite a la rutina devolver un valor al canal de llamada.
Las rutinas implementadas en distintos lenguajes tienen sus propias restricciones sobre qué tipos de datos pueden pasarse o devolverse; estos tipos de datos se indican más abajo. El tipo de datos del valor devuelto debe coincidir con el tipo de datos del valor definido para retorno 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 detalles, 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, con varias sentencias para procesar los parámetros pasados 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 a partir de un punto inicial concreto, y genera informes sobre lo que ha encontrado:
SET OutputRoot.MQMD = InputRoot.MQMD; DECLARE answer CHARACTER; SET answer = ''; CALL navigate(InputRoot.XML, answer); SET OutputRoot.XML.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
>>--"-- 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 busca tal como se describe en Despliegue de clases Java.
public static <return-type> <method-name> (<parámetros 0 - N>)
donde <return-type> debe estar en la lista de tipos de datos IN de Java en la tabla en Correlación de tipos de datos ESQL con Java (excluido el tipo REFERENCE, que no se permite como valor de retorno), o el tipo de datos vacío de Java. Los tipos de datos de parámetros también deben estar en la tabla Correlación de tipos de datos ESQL con Java. Además, no se permite que el método Java tenga una cláusula exception throws en su signatura.
Puede utilizar la API Nodo definido por el usuario (UDN) de Java en su método Java, siempre y cuando observe las restricciones que se indican en Restricciones en las rutinas Java. Para obtener más información sobre la utilización de la API UDN, 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 con el 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 ESQL siguiente para invocar myProc1:
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 ESQL siguiente 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. */ }
Tipos de datos ESQL 1 | Tipos de datos IN de Java | Tipos de datos INOUT y OUT de Java |
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.
Está permitido generar hebras dentro de un método propio. No obstante, las hebras generadas no deben utilizar las API de plug-in de Java y se debe devolver el control al intermediario.
Tenga en cuenta que todas las restricciones que se aplican al uso de la API UDN también se aplican a los métodos Java invocados desde ESQL.
La adición del archivo JAR al archivo BAR es el método más eficaz y flexible de desplegar en el intermediario.
Puede añadir un archivo JAR manualmente en el archivo BAR, a mano o de forma automática utilizando las herramientas. Las herramientas es la manera más sencilla de añadir un archivo JAR a un archivo BAR.
Si las herramientas encuentran la clase Java correcta dentro de un proyecto Java al que se hace referencia, abierto en un espacio de trabajo, compila automáticamente la clase Java en un archivo JAR y lo añade al archivo BAR. Este es el mismo procedimiento que se sigue para desplegar un nodo Compute de Java dentro de un JAR, tal como se describe en Carga de clases de nodo definido por el usuario.
Al desplegar un archivo JAR desde las herramientas, un nuevo despliegue del archivo BAR que contiene el archivo JAR hace que el flujo que se ha vuelto a desplegar vuelva a cargar las clases Java a las que se hace referencia; al igual que sucede cuando se detiene y reinicia un 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. Esto 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.
Observe que las herramientas sólo desplegarán un archivo JAR; no desplegarán un archivo de clases Java autónomo.
Este procedimiento debe efectuarse manualmente; no se pueden utilizar las herramientas.
En este método, desplegar de nuevo el flujo de mensajes no hace que se vuelvan a cargar las clases Java a las que se hace referencia; ni tampoco si se detiene y reinicia el flujo de mensajes. En este caso, la única manera de volver a cargar las clases es detener y reiniciar el intermediario mismo.
Para que el intermediario pueda encontrar 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 tiene las opciones que se muestran más arriba al desplegar el archivo JAR, al permitir que las herramientas desplieguen el archivo BAR se ofrecerá mayor flexibilidad al volver a desplegar el archivo JAR.
Las rutinas de base de datos son rutinas implementadas como procedimientos almacenados en bases de datos. Las rutinas de base de datos tienen una cláusula LANGUAGE de DATABASE y deben tener un tipo de rutina PROCEDURE.
Cuando escriba procedimientos almacenados en lenguajes como C, debe utilizar indicadores NULL para asegurarse de que el procedimiento pueda procesar correctamente los datos.
Aunque las definiciones de base de datos de un procedimiento almacenado variará entre bases de datos, el ESQL utilizado para invocarlas no varía. Los nombres asignados a los parámetros en ESQL no coinciden con los nombre proporcionados en el extremo de la base de datos. No obstante, el nombre externo de la rutina, incluida cualquier especificación de paquete o contenedor, debe coincidir con su nombre definido en la base de datos.
La cláusula DYNAMIC RESULT SET sólo se permite para rutinas de bases de datos. Sólo es necesaria si un procedimiento almacenado devuelve uno o más conjuntos de resultados. El parámetro de entero de esta cláusula debe ser 0 (cero) o más, y especifica el número de conjuntos de resultados a devolver.
Se necesita la cláusula RETURNS opcional si un procedimiento almacenado devuelve un solo valor escalar.
La cláusula EXTERNAL NAME especifica el nombre por el que la base de datos conoce a la rutina. Puede ser un nombre calificado o no calificado, en el que el calificador es el nombre del esquema de base de datos en el que se ha definido el procedimiento. Si no proporciona un nombre de esquema, se utiliza el nombre de usuario de la conexión de base de datos como el esquema en el que ubicar el procedimiento. Si el procedimiento necesario no existe en este esquema, debe proporcionar un nombre de esquema explícito, o bien en la definición de la rutina, o en la llamada (CALL) a la rutina en tiempo de ejecución. Para obtener más información sobre la elección dinámica del esquema que contiene la rutina, consulte Sentencia CALL. Cuando se utiliza un nombre cualificado, el nombre de escribirse entre comillas.
EXTERNAL NAME "mySchema.myProc";
EXTERNAL NAME "mySchema.myPackage.myProc";
Esto permite elegir dinámicamente el esquema, pero no el nombre del paquete, en la sentencia CALL.
EXTERNAL NAME "mySchema.Proc_";
A continuación se muestra una definición simple de ESQL, de un procedimiento almacenado que devuelve un solo valor escalar y un parámetro OUT:
CREATE PROCEDURE myProc1(IN P1 INT, OUT P2 INT) LANGUAGE DATABASE RETURNS INTEGER EXTERNAL NAME "myschema.myproc";
Utilice este ESQL para invocar la rutina myProc1:
/*utilizando la sintaxis de invocación de sentencia CALL*/ CALL myProc1(intVar1, intVar2) INTO intReturnVar3; /*o utilizando la sintaxis de invocación de función*/ SET intReturnVar3 = myProc1(intVar1, intVar2);
El siguiente código ESQL muestra cómo definir y llamar a procedimientos almacenados de DB2:
Definición de ESQL: 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 ) EXTERNAL NAME dbSwapParms;
Para registrar este procedimiento almacenado en DB2, copie el script siguiente en un archivo (por ejemplo, test1.sql)
-- Procedimiento almacenado de ejemplo de DB2 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 @y ejecute:
db2 -td@ -vf test1.sqldesde el indicador de mandatos de DB2.
El siguiente código ESQL muestra cómo definir y llamar a procedimientos almacenados de Oracle:
Definición de ESQL: 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 ) EXTERNAL NAME dbSwapParms;
Para registrar este procedimiento almacenado en Oracle, copie el script siguiente en un archivo (por ejemplo, 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; /y ejecute:
sqlplus <idusuario>/<contraseña> @test1.sql
El siguiente código ESQL muestra cómo definir y llamar a procedimientos almacenados de SQL Server:
Definición de ESQL: 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 ) EXTERNAL NAME dbSwapParms;
Para registrar este procedimiento almacenado en SQLServer, copie el script siguiente en un archivo (por ejemplo, test1.sql)
-- Procedimiento almacenado de ejemplo de SQLServer 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 goy ejecute:
isql -U<idusuario> -P<contraseña> -S<servidor> -d<origendatos> -itest1.sql
Si declara éstos como parámetros OUT en el ESQL, se encontrará con un error de discrepancia entre tipos durante la ejecución. Para evitar esa discrepancia, debe declarar los parámetros OUTPUT de SQL Server como parámetros INOUT en el ESQL.
El siguiente código ESQL muestra cómo definir y llamar a procedimientos almacenados de SYBASE:
Definición de ESQL: 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 ) EXTERNAL NAME dbSwapParms;
Para registrar este procedimiento almacenado en SYBASE, copie el script siguiente en un archivo (por ejemplo, test1.sql)
-- Procedimiento almacenado de ejemplo de SYBASE 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 goy ejecute:
isql -U<idusuario> -P<contraseña> -S<servidor> -d<origendatos> -itest1.sql
Si declara éstos como parámetros OUT en el ESQL, se encontrará con un error de discrepancia entre tipos durante la ejecución. Para evitar esa discrepancia, debe declarar los parámetros OUTPUT de SYBASE como parámetros INOUT en el ESQL.
Este ejemplo muestra cómo llamar a un procedimiento almacenado que devuelve dos conjuntos de resultados y un parámetro de salida:
CREATE PROCEDURE myProc1 (IN P1 INT, OUT P2 INT) LANGUAGE DATABASE DYNAMIC RESULT SETS 2 EXTERNAL NAME "myschema.myproc";
Utilice el siguiente ESQL para invocar myProc1:
/* utilizando una referencia de campo */ CALL myProc1(intVar1, intVar2, Environment.RetVal[], OutputRoot.XML.A[]) /* utilizando una variable de referencia */ CALL myProc1(intVar1, intVar2, myReferenceVariable.RetVal[], myRef2.B[])