Skip to Main Content
  • Questions
  • Passing Array of values ( integer and double) to library.

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, jairam.

Asked: December 18, 2000 - 5:55 pm UTC

Last updated: July 31, 2003 - 8:47 am UTC

Version: 8.1.6

Viewed 1000+ times

You Asked

Hi!
I have defined a C shared library on Solaris. One of the functions in the library has the prototype shown below.

double getCorrCoeff(int iNoOfObs, int iRating[], double dReturn[]);

I need to be able to call this function from pl/sql
I tried creating the following pl/sql interface to the library.
create or replace library analystIC_lib
as '/export/home/jkuppusw/analystIC/c/libcorr.so';
/
create or replace package analystIC_pkg
as
Type IntegerTabType is table of pls_integer index by binary_integer;
Type NumberTabType is table of double precision index by binary_integer;

function getCorrCoeff( a binary_integer, tRating IntegerTabType, tReturn NumberTabType )
return number;
end;
/
create or replace package body analystIC_pkg
is
function getCorrCoeff( a binary_integer, tRating IntegerTabType, tReturn NumberTabType )
return number
as language c
library analystIC_lib
name "getCorrCoeff";
end;
/
Every thing compiled fine but then i got this runtime error.
ORA-28577: argument 2 of external procedure getCorrCoeff has unsupported
datatype
ORA-06512: at "IDB.ANALYSTIC_PKG", line 0
ORA-06512: at line 17
On checking the document i found that one of the limitations is passing plsql collections to an external routine.
Given the scenario i have described is there any workaround to this limitation.
Any help in this regard would be much appreciated. Thanks in advance
Jairam

and Tom said...

You'll need to pass a collection. It will be a collection of Oracle Numbers which you can convert into C doubles. This means you will need to wrapper your existing C function:

double getCorrCoeff(int iNoOfObs, int iRating[], double
dReturn[]);

with another that converts the Oracle number type into the C double type. This new function can just call getCorrCoeff to do the work.

You'll start by defining a collection like this:

create or replace type numArray as table of number
/

And then you can have a PLSQL wrapper that looks like:

procedure pass( p_in in numArray,
p_out out numArray )
as
language C
name "pass_numArray"
library demoPassing
with context
parameters ( CONTEXT,
p_in OCICOLL,
p_in INDICATOR short,
p_out OCICOLL,
p_out INDICATOR short );

-- void pass_numArray
-- (
-- OCIExtProcContext *, /* 1 : With-Context */
-- OCIColl *, /* 2 : P_IN */
-- short , /* 3 : P_IN (Indicator) */
-- OCIColl **, /* 4 : P_OUT */
-- short * /* 5 : P_OUT (Indicator) */
-- );


Here, I'm passing an array in and getting an array out to show how to do and map the IN / OUT types. The C function that prints this out and copies the input array to the output array is:


#ifdef WIN_NT
_declspec (dllexport)
#endif
void pass_numArray
( OCIExtProcContext * ctx /* CONTEXT */,
OCIColl * p_in /* OCICOL */,
short p_in_i /* INDICATOR short */,
OCIColl ** p_out /* OCICOL */,
short * p_out_i /* INDICATOR short */
)
{
ub4 arraySize;
double tmp_dbl;
boolean exists;
OCINumber *ocinum;
int i;
myCtxStruct*myCtx;

if ( (myCtx = init( ctx )) == NULL ) return;
debugf( myCtx, "Enter Pass numArray" );

/*
* In this routine, passing a NULL array as input is an error,
* raise the error back to the calling routine
*/
if ( p_in_i == OCI_IND_NULL )
{
raise_application_error( myCtx, ERROR_ARRAY_NULL,
"Input array was NULL" );
}
else
/*
* There are a series of OCIColl* functions that operate on these
* array types. This one tells us the number of elements in the
* array
*/
if ( OCICollSize( myCtx->envhp, myCtx->errhp,
p_in, &arraySize ) != OCI_SUCCESS )
{
raise_application_error(myCtx,ERROR_OCI_ERROR,
"%s",lastOciError(myCtx));
}
else
{
debugf( myCtx, "IN Array is %d elements long",
arraySize );

/*
* Now, lets loop over the array getting each value and
* printing it out, then add that element to the output array
* to be returned to the caller
*/
for( i = 0; i < arraySize; i++ )
{
if (OCICollGetElem( myCtx->envhp, myCtx->errhp,
p_in, i,
&exists, (dvoid*)&ocinum, 0 )
!= OCI_SUCCESS )
{
raise_application_error(myCtx,ERROR_OCI_ERROR,
"%s",
lastOciError(myCtx));
break;
}
if (OCINumberToReal( myCtx->errhp, ocinum,
sizeof(tmp_dbl), &tmp_dbl ) !=
OCI_SUCCESS )
{
raise_application_error(myCtx,ERROR_OCI_ERROR,
"%s",
lastOciError(myCtx));
break;
}
debugf( myCtx, "p_in[%d] = %g", i, tmp_dbl );
if ( OCICollAppend( myCtx->envhp, myCtx->errhp,
ocinum, 0,
*p_out ) != OCI_SUCCESS )
{
raise_application_error(myCtx,ERROR_OCI_ERROR,
"%s",
lastOciError(myCtx));
break;
}
debugf( myCtx, "Appended to end of other array" );
}
/*
* And lastly, set the out parameter to NOT NULL...
*/
*p_out_i = OCI_IND_NOTNULL;
}
term(myCtx);
}


Now, I've got some utility routines in there I use in all of my extprocs that you need to ignore like init, term, debugf, raise_application_error and so on but the gist of the logic is there. The OCIColl* functions do the work for you.

Rating

  (7 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Read on rdbms/lib

A reader, May 27, 2003 - 12:24 pm UTC

Hi Tom,
Our installation doesn't have read permission on $ORACLE_HOME/rdbms/lib, so this function would not compile. Is this the default permission? We have run an argument with DBA's over security issues on this. I think the read permission was there on Oracle 8.0 and 7.3, so has this changed with 8.1 and higher?
Is there any other way (without OCI) that this function could be coded?
Thanks

Tom Kyte
May 27, 2003 - 1:03 pm UTC

why compile into rdbms lib ?

if you can code it in PLSQL -- do that.
if you can code it in Java -- do that.





Array of records

A reader, May 28, 2003 - 7:29 am UTC

Hi Tom,
Is it possible to built interface to pass array of records?
Thanks

Tom Kyte
May 28, 2003 - 7:37 am UTC

a collection of object types -- yes

create type scalar ( a number, b date, c varchar2 )
/
create type array as table of scalar
/

that could be done (no, i don't have an example, i have examples of collections of scalar types only -- that is what I use -- so I pass arrays of dates, numbers, strings -- not "records")

Create "shared library" on win 98 Oracle 8.1.7 Personal Edition ?

A reader, May 28, 2003 - 5:18 pm UTC

Hi Tom,

Will I be able to run the above OCI code (removing any external functions) on my laptop running windows 98 and Personal Oracle ? What compiler is required and can be sourced from where ? Will I be able to use the same compiler for Pro*C programs too ?

Tom Kyte
May 28, 2003 - 8:25 pm UTC

we support microsofts visual c/c++ compiler. that is what you would need.

yes for pro*c

Function returning collection

A reader, June 16, 2003 - 8:09 am UTC

Hi Tom,
Is is possible for a fuction to return a OCI collection type? Can you point me to a prototype if this is possible?
Kind regards,

Tom Kyte
June 16, 2003 - 8:18 am UTC

sure it is -- just return the ocicoll type instead of getting a pointer to a pointer of one.



OCIColl * function_name
(
OCIExtProcContext * /* 1 : With-Context */
);


Init as per session

A reader, July 30, 2003 - 6:49 am UTC

Hi Tom,
I've worked on the sample routine provided by you above.
I noticed the following:
1. For all debugf calls File is opened and closed ie. OCIFileOpen and OCIFileClose. Can this be modified to Open the file once per Oracle session and close when the session terminates?
2. For all calls to the external routine OCIFileInit is called and when the call returns the OCIFileTerm is called. Can this be modified to Init once per Oracle session and close when the session terminates?

I have a very important issue to discuss, ie. we do a legacy system connect. The connect is done first time (a global variable is kept in the myCtx to tell the connection already exists or not) the next time onwards the already made connection is used. The problem is of not been able to close the connection. Where exactly should this code of disconnect be there?

Your any possible help would be highly appreciated.

Tom Kyte
July 30, 2003 - 7:40 am UTC

well, what happens if you "crash". what happens when the file doesn't get cleaned up? what happens with buffered IO?

no, i would not suggest that. i would suggest keeping the logging the way it is.




the file stuff here is for debugging only. you would use it to debug and then disable it. we normally would not even make these calls.


You'll have to CALL the disconnect code yourself.

Thanks

A reader, July 30, 2003 - 11:36 am UTC

Thanks Tom. What we need to do is whenever the user terminates the Oracle session (this can be a logoff trigger) this explict disconnect would have to be done. The code for this will not be in the main function but a separate function, that would check the global logged on variable and if set would do the log-off/disconnect. Am I right in thinking this?

Tom Kyte
July 30, 2003 - 12:36 pm UTC

yes, the logoff trigger could be useful here for that sort of clean up

Thanks

A reader, July 31, 2003 - 8:47 am UTC

Thanks Tom, you're great.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library