Oracle FAQ | Your Portal to the Oracle Knowledge Grid |
Home -> Community -> Usenet -> c.d.o.misc -> Re: rpc problem when calling external procedure
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.
The unix lib
The Oracle lib
The function
The call
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