I’m new in pl/sq.
So, I’m trying to call pl/sql stored procedure from Java, but appears error:
wrong number or types of arguments in call to ‘searchuser’.
Where can I find most specific exception?
Is there some error logs or errors table in oracle? How can I debug these kind of problems?
Thanks!
one thing is if you call stored procedures from java: don't forget to register the function result. Example Java code:
CallableStatement cstmt = connection.prepareCall("{? = call xxx.checkArea(?,?)}");
// the 1st Parameter is the result of the function checkArea
cstmt.registerOutParameter(1, Types.INTEGER); // Function Return
cstmt.setInt(2, status); // Parameter 1 (in)
cstmt.registerOutParameter(3, Types.INTEGER); // Parameter 2 (out)
// execute the call
cstmt.execute();
// check the results
sessionId = cstmt.getInt(1); // Session-ID
rows = cstmt.getInt(3); // Rows
The PL/SQL Function is declared as following:
function checkArea(pi_area in number,
po_rows out number) return number;
Related
I am trying to make a call to a stored function in Oracle. This function not only has a return value but also has an OUT parameter. I am relatively new to Groovy but have a lot of database experience. How do I retrieve the actual values returned. Here is my sample call...assume I already have a good database connection.
I have a store function with the following signature:
GET_JOB(job_id IN VARCHAR2, max_sal OUT INTEGER) RETURNS VARCHAR2
I make the call in groovy:
def result = sql.call("{? = call get_job(?, ?)}", [Sql.VARCHAR, 'TE', Sql.NUMERIC])
This seems to run with no errors, but I am unclear as to how to retrieve the return value and the OUT parameter (second parameter)
The int Sql.call(String, List) form of the method doesn't handle output parameters. You can use void Sql.call(String, List, Closure) instead.
sql.call("{? = call get_job(?, ?)}", [Sql.VARCHAR, 'TE', Sql.NUMERIC]) { maxSal ->
// do whatever with maxSal
}
Note: If you get the return value of the stored procedure instead of the out parameter, try: sql.call(...) { ret, maxSal -> }
I'm using a product that provides a database API based on Oracle functions and I'm able to call functions via ODP.NET in general. However, I can't figure out, how to call a function that includes a Ref Cursor as Out-parameter. All the samples I found so far either call a procedure with Out-parameter or a function with the Ref Cursor as return value. I tried to define the parameters similiarly, but keep getting the error that the wrong number or type of parameters is supplied.
Here is the function header (obviously obfuscated):
FUNCTION GetXYZ(
uniqueId IN somepackage.Number_Type,
resultItems OUT somepackage.Ref_Type)
RETURN somepackage.Error_Type;
These are the type definitions in "somepackage":
SUBTYPE Number_Type IS NUMBER(13);
TYPE Ref_Type IS REF CURSOR;
SUBTYPE Error_Type IS NUMBER;
And this is the code that I have tried:
string sql = "otherpackage.GetXYZ";
var getXYZCmd = OracleCommand oracleConnection.CreateCommand(sql);
getXYZCmd.CommandType = CommandType.StoredProcedure;
getXYZCmd.Parameters.Add("uniqueId", OracleDbType.Int32).Value = uniqueExplosionId;
getXYZCmd.Parameters.Add("resultItems", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
getXYZCmd.Parameters.Add("return_value", OracleDbType.Int32).Direction = ParameterDirection.ReturnValue;
The I tried the following different ways to call the function (of course only one at a time):
var result = getXYZCmd.ExecuteNonQuery();
var reader = getXYZCmd.ExecuteReader();
var scalarResult = getXYZCmd.ExecuteScalar();
But each of them fails with the error message:
Oracle.DataAccess.Client.OracleException: ORA-06550: line 1, column 15:
PLS-00306: wrong number or types of arguments in call to 'GETXYZ'
ORA-06550: line 1, column 15:
PLS-00306: wrong number or types of arguments in call to 'GETXYZ'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored.
So is it generally possible to call a function with a Ref Cursor as Out-parameter from C# with ODP.NET? I can call a function with the same structure with a Varchar2-Out-parameter instead of the Ref Cursor without problems...
Btw, I'm using ODP.NET version 2.112.2.0 from C#.NET 3.5 in Visual Studio 2008.
Thanks in advance for your help!
You sure can. There are a few gotchas to be wary of but here is a test case
create or replace function testodpRefCursor(
uniqueId IN NUMBER
,resultItems OUT NOCOPY SYS_REFCURSOR) RETURN NUMBER
IS
BEGIN
OPEN resultItems for select level from dual connect by level < uniqueId ;
return 1;
END testodpRefCursor;
I have found that
functions likes to have the
ReturnValue as THE FIRST param
in the collection
BindByName is by default FALSE, so it defaults to BIND BY POSITION
Otherwise it is quite straight forward:
OracleCommand cmd = new OracleCommand("TESTODPREFCURSOR", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = true;
// Bind
OracleParameter oparam = cmd.Parameters.Add("ReturnValue", OracleDbType.Int64);
oparam.Direction = ParameterDirection.ReturnValue ;
OracleParameter oparam0 = cmd.Parameters.Add("uniqueId", OracleDbType.Int64);
oparam0.Value = 5 ;
oparam0.Direction = ParameterDirection.Input;
OracleParameter oparam1 = cmd.Parameters.Add("resultItems", OracleDbType.RefCursor);
oparam1.Direction = ParameterDirection.Output;
// Execute command
OracleDataReader reader;
try
{
reader = cmd.ExecuteReader();
while(reader.Read() ){
Console.WriteLine("level: {0}", reader.GetDecimal(0));
}
} ...
Now for more samples go to your Oracle Home directory and look # the Ref cursor samples in ODP.NET
for instance:
%oracle client home%\odp.net\samples\4\RefCursor
hth
I have a Java app accessing an oracle stored procedure. The arguments to the stored procedure include an array type. I do it like the following...
con = this._getConnection();
Connection narrowdConn = (Connection)WSJdbcUtil.getNativeConnection( (WSJdbcConnection)con );
callable = con.prepareCall("{call MY_PKG.MY_PROCEDURE(?, ?)}");
ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("VARCHAR2_ARR", narrowdConn);
ARRAY arrayArg1 = new ARRAY(arrayDescriptor, con, docNames);
ARRAY arrayArg2 = new ARRAY(arrayDescriptor, con, docTypes);
callable.setArray(1, arrayArg1);
callable.setArray(2, arrayArg2);
callable.execute();
Now, I am getting this Exception...
java.sql.SQLException: invalid name pattern: MY_PKG.VARCHAR2_ARR
VARCHAR2_ARR is a public TYPE, defined inside an Oracle Package like the following:
TYPE VARCHAR2_ARR IS TABLE OF VARCHAR2(50);
And used as such in my stored proc...
PROCEDURE MY_PROCEDURE
(V_ARR_ARG1 IN VARCHAR2_ARR,
V_ARR_ARG2 IN VARCHAR2_ARR)
the type VARCHAR2_ARR is a PLSQL type, you won't be able to interface it directly from java. I suggest you look into this thread on AskTom regarding a similar question.
Here are a couple suggestions:
create a SQL TYPE that you can bind from java
insert into a temporary table from java and read from it in plsql
In both cases you will have to either modify the PLSQL procedure or add a new translation procedure.
We need to set accessToUnderlyingConnectionAllowed falg true while creating a datasource
I did stuck at this problem. Hope this illustration might help you:
Steps to create the procedure in oracle.
Create type of the desired object as:
CREATE OR REPLACE TYPE STUDENT_TYPE IS OBJECT
( ID NUMBER,
NAME VARCHAR2(50));
Create another object of array as:
CREATE OR REPLACE TYPE STUDENT_ARRAY IS TABLE OF STUDENT_TYPE;
Create procedure to accepting array of object and do the desired operation, I am here just inserting the entries in table as:
CREATE OR REPLACE PROCEDURE SP_INSERT_STUDENT_RECORD_IN_BULK(
IN_STUDENT_RECORDS STUDENT_ARRAY) IS
BEGIN
FOR i IN IN_STUDENT_RECORDS.first.. IN_STUDENT_RECORDS.last
LOOP
BEGIN
INSERT INTO STUDENT(ID,NAME) VALUES
(IN_STUDENT_RECORDS(i).ID, IN_STUDENT_RECORDS(i).NAME)
END;
END LOOP:
END SP_INSERT_STUDENT_RECORD_IN_BULK;
Code snippet to call Procedure through Java
import org.springframework.jdbc.core.*;
import oracle.jdbc.OracleConnection;
....
List<Student> students = getStudentList();
try(Connection hikariCon = dataSource.getConnection()){
if(hikariCon.isWrapperFor(OracleConnection.class)){
OracleConnection con = hikariCon.unwrap(OracleConnection.class);
Object[] students = students.stream().map(student -> {
return con.createStruct(STUDENT_TYPE, new Object[]{
student.getId(),
student.getName()
});
}).collect(Collectors.list()).flatMap(List::stream).toArray();
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(dataSource)
.withProcedureName("SP_INSERT_STUDENT_RECORD_IN_BULK")
.declareParameters(
new SqlParameter("IN_STUDENT_RECORDS", Types.ARRAY));
jdbcCall.execute(con.createOracleArray(STUDENT_ARRAY,students))
}
} catch(Exception e){
log.error("Error due to- {}",e,getMessage());
}
I am trying to execute a user-defined Oracle function that returns a RefCursor using ODP.NET. Here is the function:
CREATE OR REPLACE FUNCTION PKG.FUNC_TEST (ID IN TABLE.ID%type)
RETURN SYS_REFCURSOR
AS
REF_TEST SYS_REFCURSOR;
BEGIN
OPEN REF_TEST FOR
SELECT *
FROM TABLE;
RETURN REF_TEST;
END;
/
I can call this function in Toad (select func_test(7) from dual) and get back a CURSOR. But I need to get the cursor using C# and ODP.NET to fill a DataSet, but I keep getting a NullReferenceException - "Object reference not set to an instance of an object". Here is what I have for that:
OracleConnection oracleCon = new OracleConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
OracleCommand sqlCom = new OracleCommand("select func_test(7) from dual", oracleCon);
sqlCom.Parameters.Add("REF_TEST", OracleDbType.RefCursor, ParameterDirection.ReturnValue);
OracleDataAdapter dataAdapter = new OracleDataAdapter();
dataAdapter.SelectCommand = sqlCom;
DataSet dataSet = new DataSet();
dataAdapter.Fill(dataSet); //FAILS HERE with NullReferenceException
I was able to find lots of info and samples on using stored procedures and ODP.NET, but not so much for returning RefCursors from functions.
EDIT: I do not want to explicitly add input parameters to the OracleCommand object (i.e. sqlCom.Parameters.Add("id", OracleDbType.Int32,ParameterDirection.Input).Value = 7;) as that makes it difficult to implement this as a generic RESTful web service, but I'm reserving it as my last resort but would use stored procedures instead.
Any help is much appreciated!
I think you are missing the sqlCom.ExecuteNonQuery();
also, instead of running the select func_test(7) from dual; lets switch it to run the function and pass in the param
OracleConnection oracleCon = new OracleConnection(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString);
// Set the command
string anonymous_block = "begin " +
" :refcursor1 := func_test(7) ;" +
"end;";
//fill in your function and variables via the above example
OracleCommand sqlCom= con.CreateCommand();
sqlCom.CommandText = anonymous_block;
// Bind
sqlCom.Parameters.Add("refcursor1", OracleDbType.RefCursor);
sqlCom.Parameters[0].Direction = ParameterDirection.ReturnValue;
try
{
// Execute command; Have the parameters populated
sqlCom.ExecuteNonQuery();
// Create the OracleDataAdapter
OracleDataAdapter da = new OracleDataAdapter(sqlCom);
// Populate a DataSet with refcursor1.
DataSet ds = new DataSet();
da.Fill(ds, "refcursor1", (OracleRefCursor)(sqlCom.Parameters["refcursor1"].Value));
// Print out the field count the REF Cursor
Console.WriteLine("Field count: " + ds.Tables["refcursor1"].Columns.Count);
}
catch (Exception e)
{
Console.WriteLine("Error: {0}", e.Message);
}
finally
{
// Dispose OracleCommand object
cmd.Dispose();
// Close and Dispose OracleConnection object
con.Close();
con.Dispose();}
this is based on the example ODP that can be found # %ora_home%\Client_1\ODP.NET\samples\RefCursor\Sample5.csproj
If you want to avoid (for better or worst!) the custom built param collection for each proc/function call you can get around that by utilizing anonymous blocks in your code, I have ammended (once again untested!) the code above to reflect this technique.
Here is a nice blog (from none other than Mark Williams) showing this technique.
http://oradim.blogspot.com/2007/04/odpnet-tip-anonymous-plsql-and.html
I'm trying to map OleDb parameters to an Oracle Function. I was able to do this using the System.Data.Oracle namespace but then found that this is depricated, so I thought i would re-write it as OldDb to avoid installing the Oracle Provider.
I have defined the following oracle function as an example:
create function GetImagePath (AIRSNumber in number)
return varchar2
is
begin
return '\\aiimg524\images\Ofndrtrk\2010\01\0kvrv1p000lcs74j';
end;
and I'm calling it using the following code:
using (var command = new OleDbCommand())
{
command.Connection = con;
command.CommandText = ConfigurationManager.AppSettings[OTRAK_PHOTO_FUNC];
command.CommandType = CommandType.StoredProcedure;
string parm = ConfigurationManager.AppSettings[OTRAK_PHOTO_PARM];
command.Parameters.Add(parm, OleDbType.Decimal); // maps to oracle Number
command.Parameters[parm].Direction = ParameterDirection.Input;
command.Parameters[parm].Value = airsNumber;
command.Parameters.Add(RETURN_VALUE, OleDbType.Variant); // maps to Oracle varchar2
command.Parameters[RETURN_VALUE].Direction = ParameterDirection.ReturnValue;
try
{
con.Open();
command.ExecuteNonQuery();
path = command.Parameters[RETURN_VALUE].Value.ToString();
}
I tried a bunch of different OleDB types for the parameter and the return value. the current attempt is from a mapping table i found on the web that said number = decimal and varchar2 = variant. I'm about to try every permutation of types in the enum and wanted to ask for help. the not so useful error message i get is:
[System.Data.OleDb.OleDbException] = {"ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of arguments in call to 'GETIMAGEPATH'\nORA-06550: line 1, column 7:\nPL/SQL: Statement ignored"}
This actually had nothing to do with the type of the parameters but the order. Using the OleDb provider for Oracle does not respect the names of the parameters in the parameter collection but rather the order that the parameters are added. Wwhen calling an oracle function, the return value is a free parameter that must be declared first. by adding my return value parameter and then the actual function parameter things started working.
using the command.Parameters.AddWithValue(parm, value) also simplifies things a bit.