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