Le istruzioni CREATE FUNCTION e CREATE PROCEDURE definiscono una procedura o una funzione richiamabile, generalmente detta routine.
Le istruzioni CREATE FUNCTION e CREATE PROCEDURE definiscono una procedura o una funzione richiamabile, generalmente detta routine.
Le routine sono utili per la creazione di blocchi di codice riutilizzabili che possono essere eseguiti in modo indipendente più volte. Esse possono essere implementate come una serie di istruzioni ESQL, un metodo Java oppure una procedura memorizzata del database. Tale flessibilità significa che alcune delle clausole nel diagramma di sintassi non sono applicabili (o consentite) per tutti i tipi di routine.
Ciascuna routine ha un nome, che deve essere univoco all'interno dello schema a cui appartiene. Ciò significa che i nomi delle routine non possono essere sovraccaricati; se il broker rileva che una routine è stata sovraccaricata, genera un'eccezione.
Specificare il nome della routine utilizzando la clausola RoutineName ed i parametri della routine utilizzando la clausola ParameterList. Se la clausola LANGUAGE specifica ESQL, la routine deve essere implementata utilizzando un'istruzione ESQL singola. Questa istruzione è più utile se si tratta di un'istruzione composta (BEGIN ... END) perché può contenere tutte le istruzioni ESQL necessarie per il proprio funzionamento.
In alternativa, invece di fornire un contenuto ESQL per la routine, è possibile specificare una clausola LANGUAGE diversa da ESQL. Ciò consente di utilizzare la clausola EXTERNAL NAME per fornire un riferimento al contenuto reale della routine, anche se posizionata esternamente al broker. Per ulteriori dettagli relativi all'utilizzo della clausola EXTERNAL NAME, consultare Richiamo delle procedure memorizzate e Richiamo di una routine Java.
Le routine di qualsiasi tipo LANGUAGE possono avere parametri IN, OUT ed INOUT. Ciò consente al chiamante di passare diversi valori alla routine e di ricevere diversi valori aggiornati. Tali parametri sono in aggiunta alla clausola RETURNS che può avere la routine. La clausola RETURNS consente alla routine di passare un valore al chiamante.
Le routine implementate in linguaggi differenti hanno le proprie limitazioni relative ai tipi di dati che possono essere passati o restituiti, come riportato di seguito. Il tipo di dati del valore restituito deve corrispondere al tipo di dati del valore definito per essere restituito dalla routine. Inoltre, se una routine è definita in modo da avere un valore di ritorno, questa caratteristica non può essere ignorata dal chiamante della routine. Per ulteriori dettagli, consultare Istruzione CALL.
Per qualsiasi tipo di routine o linguaggio, il metodo di richiamo della routine deve corrispondere al modo in cui la routine è stata dichiarata. Se la routine ha una clausola RETURNS, utilizzare la sintassi di richiamo di FUNCTION oppure un'istruzione CALL con una clausola INTO. Al contrario, se una routine non ha la clausola RETURNS, è necessario utilizzare un'istruzione CALL senza la clausola INTO.
Se il tipo di routine è FUNCTION, l'indicatore di direzione (IN, OUT, INOUT) è facoltativo per ciascun parametro. Tuttavia, si consiglia di specificare un indicatore di direzione per tutte le nuove routine di qualsiasi tipo.
Le variabili ESQL dichiarate come CONSTANT (o i riferimenti alle variabili dichiarati come CONSTANT) non possono avere la direzione OUT o INOUT.
Le routine ESQL sono scritte in ESQL ed hanno la clausola LANGUAGE uguale a ESQL. Il contenuto di una routine ESQL è generalmente un'istruzione composta del formato BEGIN … END, che contiene più istruzioni per l'elaborazione dei parametri passati alla routine.
CREATE PROCEDURE swapParms ( IN parm1 CHARACTER, OUT parm2 CHARACTER, INOUT parm3 CHARACTER ) BEGIN SET parm2 = parm3; SET parm3 = parm1; END;
Questa procedura di esempio illustra l'utilizzo ricorsivo di una routine ESQL. La procedura analizza una struttura ad albero, visitando tutte le posizioni al livello ed al di sotto del punto iniziale specificato e riporta le informazioni rilevate:
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;
Quando viene fornito il seguente messaggio di input:
<Person> <Name>John Smith</Name> <Salary period='monthly' taxable='yes'>-1200</Salary> </Person>
la procedura genera il seguente output, formattato 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
>>--"-- className---.---methodName--"--------------><dove className identifica la classe che contiene il metodo e methodName identifica il metodo da richiamare. Se la classe fa parte di un pacchetto, la parte relativa all'identificativo della classe deve includere il prefisso del pacchetto completo; ad esempio, "com.ibm.broker.test.MyClass.myMethod".
Per individuare la classe Java, il broker esegue una ricerca nel modo descritto in Distribuzione di classi Java.
public static <return-type> <method-name> (< 0 - N parameters>)
dove <return-type> deve essere nell'elenco dei tipi di dati IN di Java nella tabella in Mappatura da ESQL a tipo di dati Java (escluso il tipo REFERENCE, che non è consentito come valore di ritorno) oppure il tipo di dati void di Java. Anche i tipi di dati del parametro devono essere contenuti nella tabella Mappatura da ESQL a tipo di dati Java. Inoltre, il metodo Java non può contenere una clausola exception throws nella propria firma.
E0 possibile utilizzare l'API UDN (User defined Node) Java nel proprio metodo Java se si osservano le limitazioni riportate in Limitazioni per le routine Java. Per ulteriori informazioni relative all'utilizzo dell'API UDN, consultare Compilazione di un nodo definito dall'utente in Java.
Questa routine contiene tre parametri di direzione variabile e restituisce un integer che corrisponde ad un tipo di ritorno Java uguale a 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";
E' possibile utilizzare il seguente codice ESQL per richiamare myProc1:
CALL myProc1( intVar1, intVar2, intVar3) INTO intReturnVar3; -- or SET intReturnVar3 = myProc1( intVar1, intVar2, intVar3);
Questa routine contiene tre parametri di direzione variabile ed ha un tipo di ritorno Java uguale a void.
CREATE PROCEDURE myProc2( IN P1 INTEGER, OUT P2 INTEGER, INOUT P3 INTEGER ) LANGUAGE JAVA EXTERNAL NAME "com.ibm.broker.test.MyClass.myMethod2";
E' necessario utilizzare il seguente codice ESQL per richiamare myProc2:
CALL myProc2(intVar1, intVar2, intVar3);
La seguente classe Java fornisce un metodo per ciascuno degli esempi Java precedenti:
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 may or may not be NULL (depending on the value of intVar1). P2[0] is always NULL (whatever the value of intVar2). P3[0] may or may 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 may still be NULL or may have been changed intVar3 may contain the same value or may 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. */ }
Tipi di dati ESQL 1 | Tipi di dati IN Java | Tipi di dati INOUT 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 | Non supportato | Non supportato |
BOOLEAN | java.lang.Boolean | java.lang.Boolean[] |
REFERENCE (ad una struttura ad albero del messaggio) 3 4 5 6 | com.ibm.broker.plugin.MbElement | com.ibm.broker.plugin.MbElement[] (Supportato per INOUT. Non supportato per OUT) |
ROW | Non supportato | Non supportato |
LIST | Non supportato | Non supportato |
Ad esempio, se un riferimento ESQL a OutputRoot.XML.Test viene passato in un metodo Java come MbElement INOUT, ma un altro MbElement diverso viene passato a ESQL quando la chiamata ritorna, l'elemento differente deve puntare in un a posizione nella struttura ad albero OutputRoot.
Un REFERENCE ad una variabile scalare può essere utilizzato nella CALL di un metodo Java se il tipo di dati della variabile indicata dal riferimento corrisponde al tipo di dati corrispondente nella firma del programma Java.
E' possibile moltiplicare i thread all'interno del proprio metodo. Tuttavia, i metodi moltiplicati non devono utilizzare le API plug-in Java ed è necessario restituire il controllo al broker.
Notare che tutte le limitazioni valide per l'utilizzo dell'API UDN sono valide anche per i metodi Java richiamati da ESQL.
L'aggiunta del file JAR al file BAR è il metodo più efficace e flessibile di distribuzione del broker.
E' possibile aggiungere un file JAR al file BAR manualmente oppure automaticamente, utilizzando gli strumenti. L'utilizzo di strumenti è il modo più semplice per aggiungere un file JAR a un file BAR.
Se gli strumenti individuano la classe Java corretta all'interno di un progetto Java aperto nello spazio di lavoro, la classe Java Viene automaticamente compilata in un file JAR e viene aggiunta al file BAR. Questa è la stessa procedura seguita per distribuire un nodo Java Compute all'interno di un JAR, come descritto in Classloading del nodo definito dall'utente.
Quando si distribuisce un file JAR dagli strumenti, una nuova distribuzione del file BAR contenente il file JAR implica che le classi Java di riferimento vengano caricate nuovamente dal flusso ridistribuito; lo stesso funzionamento si verifica se si arresta e riavvia un flusso di messaggi che fa riferimento ad una classe Java. Assicurarsi di arrestare e riavviare (o ridistribuire) tutti i flussi che fanno riferimento al file JAR che si desidera aggiornare. In questo modo, si evita il problema di alcuni flussi in esecuzione con la versione obsoleta del file JAR ed altri in esecuzione con la nuova versione.
Notare che gli strumenti distribuiscono solo un file JAR; non distribuiscono un file di classe Java autonomo.
Questa procedura deve essere eseguita manualmente; non è possibile utilizzare gli strumenti.
In questo metodo, la ridistribuzione del flusso di messaggi non carica nuovamente le classi Java di riferimento, allo stesso modo dell'arresto e del riavvio del flusso di messaggi. L'unico modo per caricare nuovamente le classi, in questo caso, è quello di arrestare e riavviare il broker.
Per consentire al broker di individuare una classe Java, verificare che si trovi in una delle ubicazioni sopra indicate. Se il broker non riesce ad individuare la classe specificata, genera un'eccezione.
Sebbene siano disponibili le scelte sopra indicate quando si distribuisce il file JAR, consentendo agli strumenti di distribuire il file BAR, si fornisce la maggiore flessibilità quando si ridistribuisce il file JAR.
Le routine del database sono routine implementate come procedure memorizzate del database. Le routine del database hanno la clausola LANGUAGE uguale a DATABASE e devono avere il tipo di routine PROCEDURE.
Quando si scrivono procedure memorizzate in linguaggi come C, è necessario utilizzare indicatori NULL per assicurarsi che la procedura possa elaborare i dati correttamente.
Sebbene le definizioni del database di una procedura memorizzata variano tra i database, il codice ESQL utilizzato per richiamarle è uguale. I nomi assegnati ai parametri in ESQL non devono corrispondere ai nomi assegnati sul database. Tuttavia, il nome esterno della routine, incluse le specifiche del contenitore o del pacchetto, deve corrispondere al proprio nome definito nel database.
La clausola DYNAMIC RESULT SET è consentita solo per le routine del database. È obbligatoria solo se una procedura memorizzata restituisce una o più serie di risultati. Il parametro integer per questa clausola deve essere 0 (zero) o un valore maggiore e specifica il numero di serie di risultati da restituire.
La clausola facoltativa RETURNS è necessaria se una procedura memorizzata restituisce un valore scalare singolo.
La clausola EXTERNAL NAME specifica il nome con cui il database riconosce la routine. Questo può essere un nome qualificato o non qualificato, in cui il qualificatore è il nome dello schema del database in cui è definita la procedura. Se non viene fornito un nome dello schema, viene utilizzato il nome utente per la connessione al database come schema in cui individuare la procedura. Se la procedura richiesta non esiste in questo schema, è necessario fornire un nome di schema esplicito nella definizione della routine oppure nella CALL alla routine al runtime. Per ulteriori informazioni relative alla scelta dinamica dello schema che contiene la routine, consultare Istruzione CALL. Quando viene utilizzato un nome qualificato, il nome deve essere racchiuso tra apici.
EXTERNAL NAME "mySchema.myProc";
EXTERNAL NAME "mySchema.myPackage.myProc";
Ciò consente di scegliere dinamicamente lo schema, ma non il nome del pacchetto, nell'istruzione CALL.
EXTERNAL NAME "mySchema.Proc_";
Di seguito è riportato una definizione ESQL semplice di una procedura memorizzata che restituisce un valore scalare singolo ed un parametro OUT:
CREATE PROCEDURE myProc1(IN P1 INT, OUT P2 INT) LANGUAGE DATABASE RETURNS INTEGER EXTERNAL NAME "myschema.myproc";
Utilizzare questo codice ESQL per richiamare la routine myProc1:
/*using CALL statement invocation syntax*/ CALL myProc1(intVar1, intVar2) INTO intReturnVar3; /*or using function invocation syntax*/ SET intReturnVar3 = myProc1(intVar1, intVar2);
Il codice ESQL riportato di seguito illustra come definire e richiamare le procedure memorizzate DB2:
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 ) EXTERNAL NAME dbSwapParms;
Per registrare questa procedura memorizzata in DB2, copiare il seguente script in un file (ad esempio, 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 @ed eseguire:
db2 -td@ -vf test1.sqldal prompt dei comandi DB2.
Il codice ESQL riportato di seguito illustra come definire e richiamare le procedure memorizzate Oracle:
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 ) EXTERNAL NAME dbSwapParms;
Per registrare questa procedura memorizzata in Oracle, copiare il seguente script in un file (ad esempio, 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; /ed eseguire:
sqlplus <userid>/<password> @test1.sql
Il codice ESQL riportato di seguito illustra come definire e richiamare le procedure memorizzate SQL Server:
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 ) EXTERNAL NAME dbSwapParms;
Per registrare questa procedura memorizzata in SQLServer, copiare il seguente script in un file (ad esempio, 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 goed eseguire:
isql -U<userid> -P<password> -S<server> -d<datasource> -itest1.sql
Se tali parametri vengono dichiarati come parametri OUT nel codice ESQL, al runtime viene rilevato un errore di mancata corrispondenza del tipo. Per evitare tale mancata corrispondenza, è necessario dichiarare i parametri OUTPUT di SQL Server come INOUT nel codice ESQL.
Il seguente codice ESQL dimostra come definire e chiamare le procedure SYBASE memorizzate:
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 ) EXTERNAL NAME dbSwapParms;
Per registrare con SYBASE questa procedura memorizzata, copiare il seguente script in un file (ad esempio, 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 goed eseguire:
isql -U<userid> -P<password> -S<server> -d<datasource> -itest1.sql
Se tali parametri vengono dichiarati come parametri OUT nel codice ESQL, al runtime viene rilevato un errore di mancata corrispondenza del tipo. Per evitare tale mancata corrispondenza è necessario dichiarare i parametri OUTPUT di SYBASE come INOUT nel codice ESQL.
Questo esempio illustra come richiamare una procedura memorizzata che restituisce due serie di risultati ed un parametro out:
CREATE PROCEDURE myProc1 (IN P1 INT, OUT P2 INT) LANGUAGE DATABASE DYNAMIC RESULT SETS 2 EXTERNAL NAME "myschema.myproc";
Utilizzare il codice ESQL riportato di seguito per richiamare myProc1:
/* using a field reference */ CALL myProc1(intVar1, intVar2, Environment.RetVal[], OutputRoot.XML.A[]) /* using a reference variable*/ CALL myProc1(intVar1, intVar2, myReferenceVariable.RetVal[], myRef2.B[])