lunes, 4 de enero de 2010

Pro*C: dbms_output.put_line

No sé por qué nunca se me ocurrió esto, pero bueh.

Nosotros estamos acostumbrados a sacar mensajes desde un PL/SQL siempre y cuando usemos el sqlplus:

$ sqlplus ***/***
SQL> set serveroutput on; <=== para que se vean los mensajes que mostremos con el dbms_output
SQL> begin
2 dbms_output.enable(null); <=== para habilitar un buffer sin límites
3 dbms_output.put_line('Hola Quique'); <=== "muestra el texto por pantalla" (nótese las comillas)
4 end;
5 / hola PL/SQL procedure successfully completed.
SQL>
Ahora, la incognita errónea que siempre nos preguntamos o al menos yo lo hice es, cómo implementamos la instrucción "set serveroutput on" desde Pro*C, si este es un comando de sqlplus? Y ete aquí que lo que está mal formulada es la pregunta o el concepto del put_line. put_line no muestra un texto, sino que pone un texto dentro del buffer habilitado con enable, entonces ahora pensando que tenemos un almacen de textos en memoria, lo que nos queda es recuperarlo de ese almacen, y si alguna vez se te dá por leer los manuales de PL/SQL, vas a ver que el package dbms_output tiene varias rutinas, entre ellas el bendito get_line(), quien se encarga de obtener ese texto del buffer! Entonces ahora sí estoy en condiciones de utilizar el dbms_output desde Pro*C! Acá va el ejemplo:

exec sql begin declare section;
varchar vcTexto[40000];
short indTexto;
int iStat;
short indStat;
exec sql end declare section;
...
memset(&vcTexto, (int)NULL, sizeof(vcTexto));
exec sql execute
begin
dbms_output.enable(null);
dbms_output.put_line('Hola mundo desde PL/SQL');
dbms_output.get_line(:vcTexto:indTexto, :iStat:indStat);
end;
end-exec;
if(indTexto==0) printf("%s\n", vcTexto.arr);

Otro ejemplo (en este recupero el texto en otro bloque, pero como la sesion es la misma, el buffer prevalece y por ende su contenido):

exec sql begin declare section;
varchar vcTexto[40000];
short indTexto;
int iStat;
short indStat;
exec sql end declare section;
...
exec sql execute
begin
dbms_output.enable(null);
dbms_output.put_line('Hola mundo desde PL/SQL');
end-exec;
memset(&vcTexto, (int)NULL, sizeof(vcTexto));
exec sql execute
begin
dbms_output.get_line(:vcTexto:indTexto, :iStat:indStat);
end;
end-exec;
if(indTexto==0) printf("%s\n", vcTexto.arr);

Tercer y último ejemplo:
Dentro de una rutina almacenada, lo lleno de mensajes en todas las posibles salidas de error/exception.

SQL> create or replace procedure Quique is
2 begin
3 FOR i IN 1..100 LOOP
4 dbms_output.put_line(RPAD('*',1000,'*'));
5 END LOOP;
6 end;
7 /
Procedure created.
SQL>

e invoco la rutina desde el Pro*C, de esta forma:

exec sql begin declare section;
varchar vcTexto[40000];

short indTexto;
int iStat;

short indStat;
exec sql end declare section;
...
memset(&vcTexto, (int)NULL, sizeof(vcTexto));
exec sql execute
begin
dbms_output.enable(null);
Quique;
end;
end-exec;


while(iStat==0) {

memset(&vcTexto, (int)NULL, sizeof(vcTexto));
exec sql execute
begin
dbms_output.get_line(:vcTexto:indTexto, :iStat:indStat);
end;
end-exec;
if(indStat<0) istat = 1;
if(indTexto==0)
printf("%s\n", vcTexto.arr);
}

Cuando el buffer quede vacío, iStat contendrá el valor uno.
Cómo dice el manual, hay que declarar una variable huesped de tipo varchar no inferior a 32767, de lo contrario emitirá el error ora-6502.
Ojo! Si entre lecturas del buffer con get_line, se invoca un put_line, éste vacía el buffer con lo cual se pierde el resto de las lecturas previas al put_line.

http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_output.htm#BABGBACJ

No hay comentarios: