jueves, julio 21, 2005

PL/SQL y ejecuciones en host.

Por un motivo de seguridad, desde PL/SQL no es posible ejecutar llamadas al host. En principio, ningún módulo programado en PL/SQL debería ejecutar nada en sistema operativo. No obstante, seguro que cualquiera es capaz de recordar alguna situación concreta en la que poder invocar un "copy" o quizás ejecutar algún fichero .exe hubiera sido de gran ayuda.

Existe una forma de dar un rodeo a este escenario.

Oracle permite la llamada a procedimientos externos mediante un servicio extproc que debe configurarse en el fichero de alias de servicios "tnsnames.ora" para el cliente, y en el de configuración del listener "listener.ora" en el lado del servidor.

Los ficheros tendrán este aspecto.

Nota: puede haber cambios por la versión del servidor. El ejemplo se implementó sobre Oracle 8.1.7.

Fichero LISTENER.ORA
LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS_LIST =
        (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC))
      )
    )
  )
SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = extproc_agent)
      (ORACLE_HOME = E:\oracle\ora92)
      (PROGRAM = extproc)
      (ENVS = "EXTPROC_DLLS=ANY")

    )
  )


Fichero TNSNAMES.ORA
EXTPROC_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(Key = EXTPROC))
    
)
    (CONNECT_DATA =
      (SID = extproc_agent)
    )
  )


Con esta parametrización del listener, y la definición de un servicio para llamadas a 'extproc', Oracle es capaz de invocar desde PL/SQL librerías externas programadas en C o PASCAL.


Pasos:

      1.- Descarga de un compilador de C++. Este ejemplo utiliza C++Compiler de Borland.
      2.- Creación de un programa en C que ejecuta en S.O. un comando dado.
      3.- Compilación del programa e implementación de las librerías.
      4.- Desde Oracle, creación del objeto LIBRARY (biblioteca).
      5.- Creación de un procedimiento PL/SQL que invoque a una función de esa biblioteca.



Creación del programa shell.c
-----------------------------
   #include <windows.h>    
   #include <stdio.h>    
   #include <stdlib.h>      

   void __declspec(dllexport) sh(char *);    
   void sh(char *cmd)    
   {    
       system(cmd);    
   }


Compilación del código C y creación de las librerías  .lib y .dll
-----------------------------------------------------------------
  bcc32 -WD shell.c    
  implib shell.lib shell.dll    



Creación del objeto LIBRARY, en la consola de SQL.
--------------------------------------------------
SQL> create library shell_lib is 'C:\Borland\BCC55\shell.dll';
  2  /

Biblioteca creada.


De modo que ya es posible crear un procedimiento PL/SQL que referencie externamente a esa librería. Ésta se encargará de ejecutar en host el parámetro que le incluyamos. Por ejemplo, un "copy".
--------------------------------------------------------------------------------------------------
SQL> create or replace procedure shell (
  2         cmd IN varchar2)
  3  as external
  4       library shell_lib
  5       name "_sh"
  6       language C
  7       parameters (cmd string);

  8  /

Procedimiento creado.

SQL>


El testeo podrá hacerse con un simple 'exec' desde SQL*Plus, ya que equivale a "BEGIN shell('parametro'); END;"
---------------------------------------------------------------------------------------------------------------
SQL> exec shell('copy c:\test\*.* c:\destino\');

Procedimiento PL/SQL terminado correctamente.


Funciona.

17 comentarios:

Anónimo dijo...

Hola , me encuentro en una situacion similar a la que expones y he realizado todos los pasos y me funciona ciorrectamente cuando se ejecuta el procedimeinto desde el Plus no obstante cuando quiero ejecutar estod esde forms ( version 4.5) me da un error de longitud de enbtrada de parametro)..alguna pista??

Javier Morales dijo...

Creo que debes revisar bien la llamada al procedimiento. Para poder capturar mejor el error podrías crear un procedure que invocara al procedure "as external" y depurar el error y el valor de la variable.

Al tratarse de una ejecución en el host, el entorno de llamada tiene poca relevancia salvo por eso, la definición de variables de llamada.

Coméntame qué tal resulta!
Un saludo,
Javier

Anónimo dijo...

ayudame por favor javier, tengo que hacer exactamente lo que tu describes en un proyecto...baje el c++compiler (creo...!!!) el problema es que nunca he usado c++ y no conozco a nadie que lo haya usado tampoco...me podrias orientar un poco mas en este tema

Javier Morales dijo...

¿qué problema tienes exactamente?. El C++compiler no es más que un ejecutable que compila un fichero de texto con el programa en C++ que necesites (los de la web te tendrían que funcionar tal cual).

Si necesitas ayuda en algo, mándame un correo! :)

Un saludo,

Anónimo dijo...

hola javier muchas gracias por tomarte el tiempo de contestar.

La parte del c++ compiler la he pasado.... pero te escribi un nuevo correo explicandote un error que tengo ahora y te agradeceria mucho tu ayuda.

en el correo te explico lo que he hecho hasta ahora ...y el error que me da....

en verdad muchas gracias.

Anónimo dijo...

Hola Javier.

Fijate que este tema me ha ayudado muchisimo y me funciono a la perfeccion, pero ahora por razones de fuerza mayor cambiamos de un sistema operativo windows a LINUX y tengo nuevamente que hacer lo mismo es decir "ejecutar un archivo .exe desde un procedimiento pl" ...no se si tu sabes como lograrlo..te agradecer cualquier ayuda posible(aunque sea un pequeña pizca)

Gracias

Javier Morales dijo...

Hola,

Pues creo que los cambios para que funcione el programa en C++ en un entorno linux son mínimos! Fíjate:

1.- La configuración del listener, si no me equivoco, es la misma cambiando la ruta del ORACLE_HOME a la que tengáis:

LISTENEREXTPROC=
(DESCRIPTION=
(ADDRESS=
(PROTOCOL=ipc)(KEY=extproc)))
SID_LIST_LISTENEREXTPROC=
(SID_LIST=
(SID_DESC=
(PROGRAM=extproc)
(ENVS="EXTPROC_DLLS=ONLY:/home/xyz/mylib.so:/home/abc/urlib.so,
LD_LIBRARY_PATH=/private/xpm/lib:/private/mylibs,
MYPATH=/usr/ucb:/usr/local/packages,APL_ENV_FILE=/apl/conf/env.txt")
(SID_NAME=extproc)
(ORACLE_HOME=/oracle9i)))

2.- el TNSNAMES.ora así:

EXTPROC_CONNECTION_DATA=
(DESCRIPTION= (ADDRESS=(PROTOCOL=ipc)(KEY=extproc))
(CONNECT_DATA=
(SID=extproc)))

3.- El compilador de C++ ya va en el linux de forma nativa para CShell de forma que tendrías que ejecutar:

¿¿cc shell.c?? :)

(consulta con los administradores de sistemas para compilar el programa C++ según el compilador de linux que se use)

4.- Y para crear el objeto library, simplemente pasarle la ruta de la librería C++ compilada (el .lib podrás leerlo directamente)

SQL> create library shell_lib is '/home/shell.lib';
2 /

...y el resto igual!!
(cuidado, en Linux no podrás ejecutar ningún .EXE la compilación!! :) sino archivos .sh o ¿?)

Un saludo!
Javier

Anónimo dijo...

hola Javier, muchas gracias por contestarme con tanta rapidez...

seguire tus instrucciones para cambiar el codigo de windows a linux...

El problema es que el programa que tenemos es un .exe...y eso no se puede cambiar (esta hecho en visual c++ yo tengo los fuentes pero del programa no se si o puedo empaquetar en .sh).

no se si para ejecutarlo como esta pueda usar esos emuladores de windows como el wine o (el dosemu que emula solo al DOS)...

talves tu sepas algo la respecto

muchas gracias de antemano.

Anónimo dijo...

Hola Javier!

He empezado a traducir el codigo a linux...pero en la parte de fuente de c es decir el shell.c me da un error

Este es el codigo original:


Creación del programa shell.c
-----------------------------

#include <--windows.h-->
#include <--stdio.h-->
#include <--stdlib.h-->

void __declspec(dllexport) sh(char *);
void sh(char *cmd)
{
system(cmd);
}


luego con el builder lograba sacar las libreria .dll y las .pll

Pero ese codigo ahora me da error
que tengo que tendria que cambiarle para que me funcione en linux.

te agradezco como siempre tu valisa y gran ayuda.

atentamente
karla.

nota(el puse esos signos raros por que si no el html no me dejaba publicar el comentario)

Javier Morales dijo...

Hola Karla,

si consigues crear la ddl con el compilador de C++ para linux, ¿cual es exactamente el error?... ¿estás intentando ejecutar el mismo archivo .EXE que tenías en plataforma windows en el nuevo entorno linux? Te avanzo que eso no es posible!! :)

...detallame el error exacto. El programa en C es transparente a la plataforma si lo consigues compilar.

Anónimo dijo...

Hola Javier
Tengo un inconveniente con este tema y aun no logro solucionarlo por eso recurro a ti para que me guies con una posible solucion. Todos los pasos descritos en el blog sobre ejecucion de procedimientos externos ya los realice en un servidor con una base de datos de pruebas y me funciono muy bien. Debido a esto realice los mismos pasos en mi base de datos principal pero alli no me funciona y no se si es porque tengo otras utilidades funcionando. Mi base de datos es version 9i y hasta hace unos meses se le instalo el parche 9.2.0.8 para poder montar Oracle Advanced Security. Los archivos tnsnames y listener .ora estan modificados para que el Oracle Advanced Security funcione sin problemas y no incluian la parte relacionada con extproc. De todas maneras los modifique de manera tal que no afectara el inicio de la base de datos ni tampoco lo relacionado con el tema ASO.

Este es el comando que estoy ejecutando

exec shell('c:\prueba.bat');

Y este el error que me esta saliendo

ORA-28575: UNABLE TO OPEN RPC CONNECTION TO EXTERNAL PROCEDURE AGENT
ORA-06512: AT "SHELL", LINE 0
ORA-06512: AL LINE 1

Tengo entendido que este error esta relacionado con los cambios sobre los archivos tnsnames y listener .ora pero ya les he realizado varias modificaciones y no logro hacerlo funcionar.

Cualquier dato me es de gran ayuda!!

Gracias

Jorge Zarate dijo...

Hola Javier, me podrias explicar o decirme un link para poder visitar, para realizar lo siguiente:

Una consulta PL/SQL para sacar los mail de una tabla persona y despues usando un web-service para enviarlos.

* la consulta ya la tengo,
* nose como manejar un web-service desde PL/SQL, ejecutando el web desde pl.
* el web-service ya esta implementado.

gracias. saludos, Jorge

Jorge Zarate dijo...

ah, Javier, sino es mucha molestia, mi direccion es jorgezarate@gmail.com, si la respuesta tambien me la podes reponder en mi correo estaria perfecto.

No te molesto mas, Gracias, Jorge(el del comentario de arriba)

fasc05 dijo...

Hay alguna forma de optimizar el rendimiento de extproc? lo digo por que tengo una aplicación corriendo pero desde que hacen la solicitud demora varios segundos antes de recibir una respuesta, y se ha detectado que es mientras se sube el servicio de extproc

Roberto Val dijo...

Si de lo que se trata es de ejecutar un comando del sistema, este sería otro camino:

CREATE OR REPLACE FUNCTION CMD(P_CMD VARCHAR2) RETURN NUMBER
AS LANGUAGE JAVA NAME 'Shell.cmd(java.lang.String) return int';

Anónimo dijo...

HOLA, y como se haria el programa de C++ para linux

Javier Morales dijo...

Hola anónimo,

Un poco más arriba respondí a esta pregunta en los comentarios. Algo así:

Hola,

Pues creo que los cambios para que funcione el programa en C++ en un entorno linux son mínimos! Fíjate:

1.- La configuración del listener, si no me equivoco, es la misma cambiando la ruta del ORACLE_HOME a la que tengáis:

LISTENEREXTPROC=
(DESCRIPTION=
(ADDRESS=
(PROTOCOL=ipc)(KEY=extproc)))
SID_LIST_LISTENEREXTPROC=
(SID_LIST=
(SID_DESC=
(PROGRAM=extproc)
(ENVS="EXTPROC_DLLS=ONLY:/home/xyz/mylib.so:/home/abc/urlib.so,
LD_LIBRARY_PATH=/private/xpm/lib:/private/mylibs,
MYPATH=/usr/ucb:/usr/local/packages,APL_ENV_FILE=/apl/conf/env.txt")
(SID_NAME=extproc)
(ORACLE_HOME=/oracle9i)))

2.- el TNSNAMES.ora así:

EXTPROC_CONNECTION_DATA=
(DESCRIPTION= (ADDRESS=(PROTOCOL=ipc)(KEY=extproc))
(CONNECT_DATA=
(SID=extproc)))

3.- El compilador de C++ ya va en el linux de forma nativa para CShell de forma que tendrías que ejecutar:

¿¿cc shell.c?? :)

(consulta con los administradores de sistemas para compilar el programa C++ según el compilador de linux que se use)

4.- Y para crear el objeto library, simplemente pasarle la ruta de la librería C++ compilada (el .lib podrás leerlo directamente)

SQL> create library shell_lib is '/home/shell.lib';
2 /

...y el resto igual!!
(cuidado, en Linux no podrás ejecutar ningún .EXE la compilación!! :) sino archivos .sh o ¿?)

Un saludo!
Javier

Amazon