Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.misc -> Re: rpc problem when calling external procedure

Re: rpc problem when calling external procedure

From: Dyrson Rodrigues Alves Junior <dyrson_at_casasoft.com.br>
Date: Fri, 25 Jun 1999 10:23:20 -0400
Message-ID: <3773915A.F6F0E198@casasoft.com.br>


I see that your example files are using C Ansi. I don't have a C Ansi compiler. Does this affect something? Here is the code.

The C program.



#include <unistd.h>
#include <errno.h>
#include <stdio.h>

int square(x)
int x;
{
/* execl(*programa);
*/
return (x*x);
}

The unix lib



ld -b -o teste.so teste.o

The Oracle lib



create or replace library square_lib as '/u01/oracle8/teste/teste.so';

The function



create or replace function square_de(x binary_integer) return binary_integer as external
  library square_lib
  name "square"
  language C;

The call



declare
  w binary_integer;
begin
  w:=square_de(10);
  dbms_output.put_line(w);
end;

Thanks once more.

Thomas Kyte wrote:

> 
> A copy of this was sent to Dyrson Rodrigues Alves Junior
> <dyrson_at_casasoft.com.br>
> (if that email address didn't require changing)
> On Thu, 24 Jun 1999 16:59:41 -0400, you wrote:
> 
> >Dear gentleman,
> >
> >I am trying to call an external procedure but I got the following error:
> >
> >ORA-28576: lost RPC connection to external procedure agent
> >ORA-06512: at "ALUNO8.SQUARE_DE", line 0
> >ORA-06512: at line 4
> >
> 
> Lost connection means the RPC was established but then went away.  that
> indicates that the listener and tnsnames files are OK.
> 
> The lost connection typically results from a 'crash' of your extproc routine
> (its seg faulting).  If you are willing, you can try a small example I have
> below (you can also post your example PLSQL bindings and C code for us to take a
> look at since thats where the problem lies):
> 
> Here is an example routine -- the whole routine -- with some convienence
> functions I put into ALL of my extprocs for error handling and debuging:
> 
> ==================================================================================
> #include <stdio.h>
> #include <stdlib.h>
> #include <stdarg.h>
> #include <errno.h>
> #include <string.h>
> #include <time.h>
> 
> #ifndef OCI_ORACLE
> # include <oci.h>
> #endif
> 
> /*
>  * these following variables and function are used to print debug trace
>  * files.  You will enable the generation of trace files from my extproc
>  * by putting:
> 
>     (SID_DESC =
>       (SID_NAME = extproc)
>       (ORACLE_HOME = /d01/home/oracle81)
>       (PROGRAM = extproc)
>           (ENVS=DEBUG_MODULES=all,DEBUG_FILE=/tmp/ext_proc.log)
>     )
> 
>            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>  * in your listener.ora file.  the line that reads (ENVS=....) turns tracing
>  * on in my extproc (my debugf routine looks for it to be set).  You tell
>  * this extproc what file to trace to in this line as well.  this shows how
>  * to set up an environment for extprocs -- just put the VAR=VALUE,VAR=VALUE
>  * in the listener.ora file as above...
>  */
> 
> static int currLineNo;
> static char * currFileName;
> 
> void _debugf( char * fmt, ... );
> 
> #define debugf \
> currFileName=__FILE__, \
> currLineNo=__LINE__, \
> _debugf
> 
> /*
>  * the raise_application_error macro is a convienence routine i use to
>  * propagate an error back to the calling pl/sql routine.  It works alot
>  * like 'printf' would in that it takes a format string and var args.
>  * you might call it like this:
> 
>           if ( some_condition )
>                  raise_application_error( ctx, -20001, "Bummer, error" );
> 
>  * that would cause the exception to go back to the client right away,
>  * your code will be exited from (the macro does a return).  See below
>  * for an example use of this (in the code)
>  */
> 
> #define raise_application_error return raise_application_error_x
> 
> static long raise_application_error_x( OCIExtProcContext * ctx,
>                                      int errCode,
>                                      char * errMsg, ...)
> {
> char    msg[8192];
> va_list ap;
> 
>     va_start(ap,errMsg);
>     vsprintf( msg, errMsg, ap );
>     va_end(ap);
> 
>     debugf( "raise application error( %d, %s )", errCode, msg );
>     OCIExtProcRaiseExcpWithMsg(ctx,errCode,msg,strlen(msg));
>     return -1;
> }
> 
> /*
>  * convienence routine for printing out strings and numbers passed to
>  * your extproc routine.  I like to put at the head of each routine
>  * something like:
> 
>         debugf( "entering ROUTINE_NAME" );
>         debug_str( "parm1 = ", parm1_i, parm1, parm1_l );
>         debug_num( "parm2 = ", parm2_i, parm2 );
>  *
>  * that way, if i enable trace, i get to see all of my inputs in the trace
>  * file
>  */
> 
> static void debug_str( char * msg, short ind, char * data, int len )
> {
>     if ( ind ) { debugf( "%s is NULL", msg ); }
>     else { debugf( "%s (%d bytes) = %.*s", msg, len, len, data ); }
> }
> 
> static void debug_num( char * msg, short ind, int data )
> {
>     if ( ind ) { debugf( "%s is NULL", msg ); }
>     else { debugf( "%s = %d", msg, data ); }
> }
> 
> /*
>  * here is the routine.  everything above and below this routine is
>  * 'boilerplate' (reusable) code.  I always pass the context into the
>  * routine for raise_application_error.  I also ALWAYS pass the indicator
>  * so I can look for NULLS.  This function is set up to accept:
> 
>         - the calling context
>         - a string along with its indicator variable and length
>         - a pointer to a short so I can tell the caller whether the return
>           value is 'set' or NULL.  If I set *return_indicator to 1, the return
>           value is ignored and NULL is returned.  set it to zero and the return
>           value is returned
> 
>  */
> 
> long
> printhello( OCIExtProcContext  * ctx,
>             char * p_uname, short p_uname_i, int p_uname_l,
>                         short * return_indicator )
> {
> FILE    * fp;
> 
>         debugf( "enter printhello" );
>         debug_str( "p_uname", p_uname_i, p_uname, p_uname_l );
> 
>         if ( (fp=fopen("/tmp/hellofile.txt", "w")) == NULL )
>         {
>                 raise_application_error( ctx, -20001, "error opening file %d (%s)",
>                                                                  errno, strerror(errno) );
>         }
>         debugf( "opened file" );
> 
>         if ( p_uname_i != 0 )
>         {
>                 fprintf( fp, "Your argument was NULL\n" );
>         }
>         else
>         {
>                 fprintf( fp, "You sent me '%s'\n", p_uname );
>         }
>         fclose( fp );
> 
>         *return_indicator = 0;
>         return 1234;
> }
> 
> /*
>  * this is the real implementation of 'debugf'
>  */
> 
> /* ------------------------------------- */
> #undef debugf
> 
> void _debugf( char * fmt, ... )
> {
> va_list ap;
> FILE    * fp;
> char    * debug_modules = getenv( "DEBUG_MODULES" );
> char    * debug_file    = getenv( "DEBUG_FILE" );
> time_t  theTime = time(NULL);
> char    buffer[25];
> 
>     if ( !debug_modules ) return;
> 
>     if ( (fp = fopen( debug_file, "at" )) == NULL ) fp = stderr;
> 
>         strftime( buffer, sizeof(buffer), "%y%m%d %H%M%S GMT", gmtime(&theTime) );
> 
>     fprintf( fp, "%s (%13s,%3d) ", buffer, currFileName, currLineNo );
>     va_start(ap,fmt);
>     vfprintf( fp, fmt, ap );
>     va_end(ap);
>     fprintf( fp, "\n" );
> 
>     if ( fp != stderr ) fclose(fp);
> }
> 
> ==================================================================================
> 
> And here is the makefile I used (assuming you save the above source code to
> extproc.c)
> 
> ==================================================================================
> MAKEFILE= $(ORACLE_HOME)/rdbms/demo/demo_rdbms.mk
> 
> INCLUDE= -I$(ORACLE_HOME)/rdbms/demo \
>          -I$(ORACLE_HOME)/rdbms/public \
>          -I$(ORACLE_HOME)/plsql/public \
>          -I$(ORACLE_HOME)/network/public
> 
> all: extproc.so
> 
> include $(MAKEFILE)
> 
> extproc.so: extproc.c extproc.o
>     $(MAKE) -f $(MAKEFILE) extproc_callback \
>      SHARED_LIBNAME=extproc.so OBJS="extproc.o"
> 
> CFLAGS= -g -I. $(INCLUDE) -Wall
> ==================================================================================
> 
> This is my extproc.sql file to create the bindings to C
> 
> ==================================================================================
> CREATE OR REPLACE PACKAGE my_extproc_pkg
> as
>     function printhello( p_uname in varchar2 ) return binary_integer;
>     pragma restrict_references( printhello, rnds, wnds, rnps, wnps );
> 
>     pragma restrict_references( my_extproc_pkg, rnds, wnds, rnps, wnps );
> end;
> /
> show errors
> 
> drop library my_extproc_lib
> /
> create library my_extproc_lib as '/export/home/tkyte/beehive/examp/extproc.so'
> /
> 
> CREATE OR REPLACE PACKAGE BODY my_extproc_pkg
> as
> 
>     function printhello( p_uname in  varchar2 ) return binary_integer
>     is external
>     name "printhello"
>     library my_extproc_lib
>     language C
>     with context
>     parameters ( CONTEXT,
>                  p_uname  STRING,
>                  p_uname  INDICATOR short,
>                  p_uname  LENGTH int,
>                  RETURN   INDICATOR short );
> 
> end;
> /
> ==================================================================================
> 
> Notice:
> 
> - in the body, we:
>   - name "printhello"   -- important to quote that name AND make sure
>                         -- you have a function named that in the .so
>                         -- file. you had main in the .so but printhello
>                         -- in the body....
> 
>   - with context        -- you pretty much ALWAYS want to send the context
>                         -- if you want to be able to do error handling
> 
>   - p_uname INDICATOR   -- important to send null indicators for ALL variables.
>                         -- if not and a pl/sql routine gives you a NULL, you
>                         -- will NOT be able to detect it.
> 
>   - p_uname LENGTH      -- lengths are always sent with strings.  You need to
>                         -- have a variable in your c routine to accept it
> 
>   - RETURN INDICATOR    -- if you want to be able to return NULL or a value, you
>                         -- must include this.
> 
> So, now that I've done the above, I can run:
> 
> SQL> select my_extproc_pkg.printhello( 'Hello there' ) from dual
>   2  /
> 
> MY_EXTPROC_PKG.PRINTHELLO('HELLOTHERE')
> ---------------------------------------
>                                    1234
> 
> and it returns 1234 as expected.  Additionally:
> 
> $ ls -l /tmp/hellofile.txt
> -rw-rw-r--   1 oracle81       26 Feb 18 08:23 /tmp/hellofile.txt
> 
> $ cat /tmp/hellofile.txt
> You sent me 'Hello there'
> 
> and if I set up debugging in the listener.ora file I can also see:
> 
> $ ls -l /tmp/ext_proc.log
> -rw-rw-r--   1 oracle81      176 Feb 18 08:25 /tmp/ext_proc.log
> 
> $ cat /tmp/ext_proc.log
> 990218 132509 GMT (    extproc.c,126) enter printhello
> 990218 132509 GMT (    extproc.c, 94) p_uname (11 bytes) = Hello there
> 990218 132509 GMT (    extproc.c,134) opened file
> 
> you will find this *extremely* useful to debug your extprocs.  I would suggest
> you heavily instrument the code with debugf calls to aid you in fixing
> problems....
> 
> In the event you hit an 'rpc' error in the next steps (eg: the listner is
> configured wrong), the following might help as well:
> 
> You need to setup the listener.ora and tnsnames.ora file for extprocs.
> 
> Here is a sample listener.ora file:
> 
> ----------------------------------------------------
> LISTENER =
>   (ADDRESS_LIST =
>     (ADDRESS=(PROTOCOL=IPC)(KEY=oracle81.world))
>     (ADDRESS=(PROTOCOL=TCP)(Host=sheepdog)(Port=1521))
>   )
> SID_LIST_LISTENER =
>   (SID_LIST =
>     (SID_DESC =
>       (GLOBAL_DBNAME=sheepdog.us.oracle.com)
>       (ORACLE_HOME= /d01/home/oracle81)
>       (SID_NAME = oracle81)
>     )
>     (SID_DESC =
>       (SID_NAME = extproc)
>       (ORACLE_HOME = /d01/home/oracle81)
>       (PROGRAM = extproc)
>       #(ENVS=DEBUG_MODULES=all,DEBUG_FILE=/tmp/test/ext_proc.log)
>     )
>   )
> STARTUP_WAIT_TIME_LISTENER = 0
> CONNECT_TIMEOUT_LISTENER = 10
> ----------------------------------------------------
> 
> The important things in the listener file for extprocs:
> 
> o ADDRESS=(PROTOCOL=IPC)(KEY=oracle81.world))
> 
> set up an IPC based listener.  remember the value of the KEY= (you can make it
> whatever you want, just remember what it is)
> 
> o (SID_DESC=(SID_NAME=extproc)
> 
> remember that SID_NAME, call it extproc.
> 
> o  (ENVS=DEBUG_MODULES=all,DEBUG_FILE=/tmp/test/ext_proc.log)
> 
> if you want a specific set of environment variables setup for your extproc
> routines, put them here, separated by commas.  The above for example, sets up 2
> environment variables for the extproc servcies, DEBUG_MODULES and DEBUG_FILE.
> Since extprocs run detached from a console and are a little touchy to debug,
> having copious amounts of 'tracing' that you can enable/disable via the
> environment is suggested (by me)...
> 
> The next file is the tnsnames.ora file, it needs an entry like:
> 
> ---------------------------------------------------
> extproc_connection_data =
>   (DESCRIPTION =
>     (ADDRESS = (PROTOCOL = IPC)(KEY = oracle81.world))
>     (CONNECT_DATA = (SID = extproc))
>   )
> --------------------------------------------------
> 
> Important things:
> 
> o (ADDRESS = (PROTOCOL = IPC)(KEY = oracle81.world))
> 
> should be the same as was in the listener.ora file....
> 
> o (CONNECT_DATA = (SID = extproc))
> 
> the sid must match the sid in the (SID_DESC=(SID_NAME=extproc) from the
> listener.ora....
> 
> Also, if your sqlnet.ora specifies some default domain, it needs to be on the
> tnsnames entry.  So if you have a sqlnet.ora with stuff like:
> 
> sqlnet.authentication_services=(none)
> names.directory_path = (TNSNAMES, HOSTNAME)
> names.default_domain = world
> name.default_zone = world
> automatic_ipc = off
> 
> in it, that would be extproc_connection_data.world -- not just
> extproc_connection_data.
> 
> hope this helps...
> 
> >I followed every step described in the bulletin PLSQL:Oracle8 External
> >Procedure Calls. So I donīt understand what is the problem.
> >
> >I am using HP-UX B.10.20 with Oracle 8.0.4.
> >
> >The tnsnames.ora and listener.ora of my server are below. Thanks in
> >advance.
> >
> ># Filename: Tnsnames.ora
> >#
> >extproc_connection_data =
> >  (DESCRIPTION =
> >    (ADDRESS = (PROTOCOL = IPC)(KEY = O8))
> >    (CONNECT_DATA = (SID = extproc))
> >  )
> >
> >O8 =
> >  (DESCRIPTION =
> >    (ADDRESS = (PROTOCOL= TCP)(Host= oaec)(Port= 1521))
> >    (CONNECT_DATA = (SID = O8))
> >  )
> >
> >
> ># Filename: Listener.ora
> >#
> >LISTENER =
> >  (ADDRESS_LIST =
> >        (ADDRESS= (PROTOCOL= IPC)(KEY= O8))
> >        (ADDRESS= (PROTOCOL= IPC)(KEY= PNPKEY))
> >        (ADDRESS= (PROTOCOL= TCP)(Host= 144.23.10.5)(Port= 1521))
> >        (ADDRESS= (PROTOCOL= IPC)(KEY= extproc))
> >  )
> >SID_LIST_LISTENER =
> >  (SID_LIST =
> >    (SID_DESC =
> >      (ORACLE_HOME= /u01/oracle8/product/8.0.4)
> >      (SID_NAME = O8)
> >    )
> >    (SID_DESC =
> >      (SID_NAME = extproc)
> >      (ORACLE_HOME = /u01/oracle8/product/8.0.4)
> >      (PROGRAM = extproc)
> >    )
> >  )
> >STARTUP_WAIT_TIME_LISTENER = 0
> >CONNECT_TIMEOUT_LISTENER = 60
> >TRACE_LEVEL_LISTENER = off
> 
> --
> See http://govt.us.oracle.com/~tkyte/ for my columns 'Digging-in to Oracle8i'...
> Current article is "Part I of V, Autonomous Transactions" updated June 21'st
> 
> Thomas Kyte                   tkyte_at_us.oracle.com
> Oracle Service Industries     Reston, VA   USA
> 
> Opinions are mine and do not necessarily reflect those of Oracle Corporation

--

+-------------------------------+----------------------------------+
| Dyrson Rodrigues Alves Junior | Casa de Software                 |
| Analista de Sistemas          | Oracle Approved Education Center |
| mailto:dyrson@casasoft.com.br | http://www.casasoft.com.br       |
+-------------------------------+----------------------------------+

Received on Fri Jun 25 1999 - 09:23:20 CDT

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US