You Asked
Tom,
Thanks for taking time to answer the questions. I have question about the implict cursors are not closed after the Java PrepareCall statement is called. Following is the test case to illustrate the issue (I'm modified your example somewhat to demostrate),
create or replace package demo_pkg
as
type refcur is ref cursor;
procedure get_cur( x in out refcur );
end;
/
create or replace package body demo_pkg
as
procedure get_cur( x in out refcur )
is
l_user varchar2(1000);
begin
open x for select USER from dual;
select user
into l_user
from dual where rownum = 1;
select user
into l_user
from dual where rownum = 1;
end;
end;
/
import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;
class curvar
{
public static void showOpenCnt( Connection conn, String msg)
throws SQLException
{
Statement stmt = conn.createStatement();
ResultSet rset =
stmt.executeQuery
( "select a.value, b.name "+
"from v$mystat a, v$statname b "+
"where a.statistic# = b.statistic# "+
"and a.statistic#= 3");
System.out.println( "====================================" );
System.out.println( msg );
System.out.println( "====================================" );
System.out.println( " " );
while( rset.next() )
System.out.println( rset.getString(1)+ " " +
rset.getString(2) );
System.out.println( "-----------------------" );
rset.close();
stmt.close();
Statement stmt1 = null;
ResultSet rset1 = null;
stmt1 = conn.createStatement();
rset1 = stmt1.executeQuery("select sid, sql_text from v$open_cursor where sid = (select a.sid "+
"from v$mystat a, v$statname b "+
"where a.statistic# = b.statistic# "+
"and a.statistic#= 3 "+
"and rownum = 1)");
System.out.println("Open Cursors Currently");
while (rset1.next()) {
System.out.println("SID***" + rset1.getInt(1) + " " + rset1.getString(2) );
}
System.out.println( "-----------------------" );
System.out.println( " " );
rset1.close();
stmt1.close();
}
public static void main (String args [])
throws SQLException, ClassNotFoundException
{
String query =
"begin demo_pkg.get_cur( :1 ); end;";
DriverManager.registerDriver
(new oracle.jdbc.driver.OracleDriver());
Connection conn=
DriverManager.getConnection
("jdbc:oracle:oci8:@ora8idev",
"scott", "tiger");
showOpenCnt( conn, "Before Anything" );
CallableStatement cstmt = conn.prepareCall(query);
cstmt.registerOutParameter(1,OracleTypes.CURSOR);
cstmt.execute();
showOpenCnt( conn, "After prepare and execute" );
ResultSet rset = (ResultSet)cstmt.getObject(1);
showOpenCnt( conn,
"After prepare and execute and getObject" );
for(int i = 0; rset.next(); i++ )
{
if ( i == 10 )
showOpenCnt(conn,"After fetching 10 rows...");
}
showOpenCnt( conn, "After but before any close" );
rset.close();
showOpenCnt( conn, "After ResultSet closes" );
cstmt.close();
showOpenCnt( conn, "After CallableStatement closes" );
}
}
Here is the results,
C:\demo>java curvar
====================================
Before Anything
====================================
1 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
-----------------------
====================================
After prepare and execute
====================================
5 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 begin demo_pkg.get_cur( :1 ); end;
SID***52 SELECT USER FROM DUAL
-----------------------
====================================
After prepare and execute and getObject
====================================
5 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 begin demo_pkg.get_cur( :1 ); end;
SID***52 SELECT USER FROM DUAL
-----------------------
====================================
After but before any close
====================================
5 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 begin demo_pkg.get_cur( :1 ); end;
SID***52 SELECT USER FROM DUAL
-----------------------
====================================
After ResultSet closes
====================================
4 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 begin demo_pkg.get_cur( :1 ); end;
-----------------------
====================================
After CallableStatement closes
====================================
3 opened cursors current
-----------------------
Open Cursors Currently
SID***52 select sid, sql_text from v$open_cursor where sid = (select
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
SID***52 SELECT USER FROM DUAL WHERE ROWNUM = 1
-----------------------
As you can see the two identical "SELECT USER FROM DUAL WHERE ROWNUM = 1" SQL statements in the procedure are still open after the PrepareCallstatement closed.
So, is it correct? Could you explain and help to solve the problem since if there is loop and OPEN_CURSURS set to 500 or 1000, it is easy to get ORA-01000 error.
Thanks
Twin Dophin.
and Tom said...
These are cached by PLSQL for performance.
They will remain there until you run out of slots in your OPEN CURSOR array -- at which point they are flushed if not currently being used (plsql lets them "go away" if and when the server needs that slot)
They do not count against you, they are there for performance. It is an EXCELLENT reason why most java programs entire suite of SQL should consist of nothing more then begin .... end; -- never any actual DML of its own. More manageable, more flexible.
You can test this out yourself by using this:
create or replace package demo_pkg
as
type refcur is ref cursor;
procedure get_cur( x in out refcur );
end;
/
create or replace package body demo_pkg
as
g_first_time boolean default true;
procedure get_cur( x in out refcur )
is
l_user varchar2(1000);
begin
open x for select USER from dual THIS_IS_A_JAVA_CURSOR;
if ( g_first_time )
then
select user
into l_user
from dual THIS_IS_PLSQL where rownum = 1;
select user
into l_user
from dual THIS_TOO_IS_PLSQL where rownum = 1;
g_first_time := false;
end if;
end;
end;
/
that plsql only needs the cursors for a bit -- we don't need them everytime...
Now I modified the java to be:
public static void main (String args [])
throws SQLException, ClassNotFoundException
{
String query =
"begin demo_pkg.get_cur( :1 ); end;";
DriverManager.registerDriver
(new oracle.jdbc.driver.OracleDriver());
Connection conn=
DriverManager.getConnection
("jdbc:oracle:oci8:@ora817dev",
"scott", "tiger");
showOpenCnt( conn, "Before Anything" );
CallableStatement cstmt = conn.prepareCall(query);
cstmt.registerOutParameter(1,OracleTypes.CURSOR);
for( int j = 0; j < 100; j++ )
{
cstmt.execute();
showOpenCnt( conn, j + ") After prepare and execute" );
ResultSet rset = (ResultSet)cstmt.getObject(1);
for(int i = 0; rset.next(); i++ );
}
cstmt.close();
showOpenCnt( conn, "After CallableStatement closes" );
}
I don't close the result sets - we just let them leak all over the place. I have open_cursors set to 50 and run:
> !java
java curvar
====================================
Before Anything
====================================
1 opened cursors current
-----------------------
Open Cursors Currently
SID***8 SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8 ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
-----------------------
====================================
0) After prepare and execute
====================================
5 opened cursors current
-----------------------
Open Cursors Currently
SID***8 SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 SELECT USER FROM DUAL THIS_IS_PLSQL WHERE ROWNUM = 1
SID***8 begin demo_pkg.get_cur( :1 ); end;
SID***8 ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8 ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8 SELECT USER FROM DUAL THIS_TOO_IS_PLSQL WHERE ROWNUM = 1
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------
====================================
1) After prepare and execute
====================================
6 opened cursors current
-----------------------
Open Cursors Currently
SID***8 SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 SELECT USER FROM DUAL THIS_IS_PLSQL WHERE ROWNUM = 1
SID***8 begin demo_pkg.get_cur( :1 ); end;
SID***8 ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8 ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8 SELECT USER FROM DUAL THIS_TOO_IS_PLSQL WHERE ROWNUM = 1
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------
note that after each iteration I got more and more "this is a java cursor". The plsql guys stayed in there.... UNTIL:
====================================
45) After prepare and execute
====================================
50 opened cursors current
-----------------------
Open Cursors Currently
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 SELECT USER FROM DUAL THIS_IS_PLSQL WHERE ROWNUM = 1
SID***8 begin demo_pkg.get_cur( :1 ); end;
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8 SELECT USER FROM DUAL THIS_TOO_IS_PLSQL WHERE ROWNUM = 1
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
<lots of those chopped out>
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------
====================================
46) After prepare and execute
====================================
49 opened cursors current
-----------------------
Open Cursors Currently
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 begin demo_pkg.get_cur( :1 ); end;
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
<lots chopped NOTE: PLSQL cursors *gone*>
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------
====================================
47) After prepare and execute
====================================
50 opened cursors current
-----------------------
Open Cursors Currently
SID***8 select a.value, b.name from v$mystat a, v$statname b where a
SID***8 begin demo_pkg.get_cur( :1 ); end;
SID***8 select sid, sql_text from v$open_cursor where sid = (select
SID***8 ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
...
SID***8 SELECT USER FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------
java.sql.SQLException: ORA-01000: maximum open cursors exceeded
at java.lang.Throwable.<init>(Compiled Code)
at java.lang.Exception.<init>(Compiled Code)
at java.sql.SQLException.<init>(Compiled Code)
at oracle.jdbc.dbaccess.DBError.throwSqlException(Compiled Code)
at oracle.jdbc.oci8.OCIDBAccess.check_error(Compiled Code)
at oracle.jdbc.oci8.OCIDBAccess.parseExecuteDescribe(Compiled Code)
at oracle.jdbc.driver.OracleStatement.doExecuteQuery(Compiled Code)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(Compiled Code)
at oracle.jdbc.driver.OracleStatement.executeQuery(Compiled Code)
at curvar.showOpenCnt(Compiled Code)
at curvar.main(Compiled Code)
Rating
(2 ratings)
Is this answer out of date? If it is, please let us know via a Comment