Why is this package leaking open cursors? - oracle

In our application, we call various stored procedures that return reference cursors in the following fashion:
SELECT foo_package.sp_Bar('arg1', 'arg2', 'arg3', 'arg4') FROM dual;
What the wrap_xml function does is to convert the cursor result to an XML type, which will then be used in the application. After the conversion, it immediately closes the cursor (this step has solved memory issues for us before).
FUNCTION wrap_xml (c_result SYS_REFCURSOR)
RETURN XMLTYPE
IS
xml_val XMLTYPE;
BEGIN
xml_val := xmltype.CreateXML (c_result);
IF c_result%ISOPEN
THEN
CLOSE c_result;
END IF;
RETURN xml_val;
END;
In most cases, this appears to work fine: the XML is created and the cursors are closed. However, since introducing a stored procedure that opens a cursor for a dynamic query, we are observing a rapid increase in open cursors, eventually leading to:
ORA-01000: maximum open cursors exceeded
The dynamic query is built in order to "simulate" the results returned from other cursors, for testing purposes. For example, the stored procedure would build a dynamic query like this:
SELECT '1' as "COLUMN1", '990' as "COLUMN2", 'N' as "COLUMN3", NULL as "COLUMN5" FROM dual;
It would then open a cursor for this query string, and return the cursor:
OPEN rc_return FOR v_sql_query;
RETURN rc_return;
The resulting reference cursor is again passed to the wrap_xml function above, which I would expect to close the cursor – just like it does any other cursor. However, this doesn't seem to be the case, as the number of open cursors just keeps on growing. What could be the reason for this?
Additional investigation:
Stepping through the wrap_xml function, I can see the program flow skipping over the body of the c_result%ISOPEN check, implying that the cursor has indeed been closed. Yet it appears that the open cursor count still increases!

We appear to have plugged the leak by removing the ISOPEN check from the wrap_xml function and just executing the close cursor command in all cases. Apparently the ISOPEN flag is not set on cursors opened for dynamic SQL queries.
I am, however, unable to find a reference for this. Can anyone back this up?

Related

Is it mandatory to close cursor in PLSQL block

Please see the below procedure. I did not close cursor and it is working fine. Am I missing something here.
Is it mandatory to close cursor or Oracle closes it automatically?
CREATE OR REPLACE PROCEDURE pr_cursor_test IS
CURSOR emp_cur IS SELECT empno, ename FROM emp;
emprec emp_cur%rowtype;
BEGIN
OPEN emp_cur;
LOOP
FETCH emp_cur INTO emprec;
EXIT WHEN
emp_cur%notfound;
--Do Something
dbms_output.put_line(emprec.ename);
END LOOP;
END;
This article on Oracle website is pretty clear about it.
When you declare a cursor in a package (that is, not inside a
subprogram of the package) and the cursor is opened, it will stay open
until you explicitly close it or your session is terminated.
When the cursor is declared in a declaration section (and not in a
package), Oracle Database will also automatically close it when the
block in which it is declared terminates. It is still, however, a good
idea to explicitly close the cursor yourself. If the cursor is moved
to a package, you will have the now necessary CLOSE already in place.
And if it is local, then including a CLOSE statement will also show
other developers and your manager that you are paying attention.
In your code example CURSOR is declared as part of procedure (not as part of package), which means, cursor is closed automatically when procedure executioin is complete. However it is best coding practice to CLOSE cursor statement if you have OPEN cursor statement in code. It makes code easy to understand and support in future.

Getting error writing an anonymous block in TOAD DB2

I am new to DB2. I want to execute an anonymous black in toad.
BEGIN ATOMIC
DECLARE TEMP_SCHEMA VARCHAR(12) ;
SET TEMP_SCHEMA = 'SCHEMA1';
SELECT * FROM TEMP_SCHEMA.TABLE_NAME
WHERE 1=1
WITH UR;
END;
I am getting following error:
20159: [IBM][DB2/AIX64] SQL20159W The isolation clause is ignored because of the statement context.
Can you please help.
According to the documentation at https://www.ibm.com/support/knowledgecenter/en/SSMKHH_10.0.0/com.ibm.etools.mft.doc/ak04940_.htm?view=embed , "If ATOMIC is specified, only one instance of a message flow (that is, one thread) is allowed to execute the statements of a specific BEGIN ATOMIC... END statement (identified by its schema and label), at any one time. If no label is present, the behavior is as if a zero length label had been specified.
The BEGIN ATOMIC construct is useful when a number of changes need to be made to a shared variable and it is important to prevent other instances seeing the intermediate states of the data."
Using ATOMIC in a stored procedure means that your code will execute as a singleton, providing maximal isolation. That would be in direct conflict with your "WITH UR" isolation option. Even though you are using the ATOMIC keyword in a script, not in a stored procedure, DB2 still treats it as a single thread, so it will complain if you include query hints that attempt to lower the isolation level.
After removing the ATOMIC keyword, you are getting the token error because your SELECT * FROM TEMP_SCHEMA.TABLE_NAME
WHERE 1=1 statement is attempting to return a result set to Toad from inside a BEGIN block. Unfortunately, this is not possible in DB2. As soon as you have any procedural code, which forces you to utilize a BEGIN block, DB2 steadfastly refuses to return data to the client. The only way that I found to return results from inside a BEGIN block is to place the BEGIN block in a stored procedure, and then use a CURSOR to return the result set to Toad, e.g.,
BEGIN
DECLARE C1 CURSOR WITH RETURN WITH HOLD FOR SELECT * FROM .EMPLOYEE;
OPEN C1;
END;
Note that you must enclose the CURSOR code in an inner BEGIN block, which DB2 requires when sending a result set back to the client WITH HOLD.
If you want to return the value of a variable to Toad from your prototype stored procedure, you can use this approach:
BEGIN
DECLARE C1 CURSOR WITH RETURN WITH HOLD FOR
SELECT * FROM TABLE(SELECT * FROM (VALUES(<variable goes here>))
AS TEMP(<descriptive name for the variable goes here>)) AS TEMP1;
OPEN C1;
END;
To summarize, in order to use the Anonymous Block as a prototyping vehicle in a development tool such as Toad, you have to wrap it in a stored procedure if you want to return any results, and you must use a CURSOR embedded in an inner BEGIN block to do so. Its unfortunate that DB2 is so much more cumbersome than MS SQL Server in this regard.

Return data rows from a pl/sql block

I want to write pl/sql code which utilizes a Cursor and Bulk Collect to retrieve my data. My database has rows in the order of millions, and sometimes I have to query it to fetch nearly all records on client's request. I do the querying and subsequent processing in batches, so as to not congest the server and show incremental progress to the client. I have seen that digging down for later batches takes considerably more time, which is why I am trying to do it by way of cursor.
Here is what should be simple pl/sql around my main sql query:
declare
cursor device_row_cur
is
select /my_query_details/;
type l_device_rows is table of device_row_cur%rowtype;
out_entries l_device_rows := l_device_rows();
begin
open device_row_cur;
fetch device_row_cur
bulk collect into out_entries
limit 100;
close device_row_cur;
end;
I am doing batches of 100, and fetching them into out_entries. The problem is that this block compiles and executes just fine, but doesn't return the data rows it fetched. I would like it to return those rows just the way a select would. How can this be achieved? Any ideas?
An anonymous block can't return anything. You can assign values to a bind variable, including a collection type or ref cursor, inside the block. But the collection would have to be defined, as well as declared, outside the block. That is, it would have to be a type you can use in plain SQL, not something defined in PL/SQL. At the moment you're using a PL/SQL type that is defined within the block, and a variable that is declared within the block too - so it's out of scope to the client, and wouldn't be a valid type outside it either. (It also doesn't need to be initialised, but that's a minor issue).
Dpending on how it will really be consumed, one option is to use a ref cursor, and you can declare and display that through SQL*Plus or SQL Developer with the variable and print commands. For example:
variable rc sys_refcursor
begin
open :rc for ( select ... /* your cursor statement */ );
end;
/
print rc
You can do something similar from a client application, e.g. have a function returning a ref cursor or a procedure with an out parameter that is a ref cursor, and bind that from the application. Then iterate over the ref cursor as a result set. But the details depend on the language your application is using.
Another option is to have a pipelined function that returns a table type - again defined at SQL level (with create type) not in PL/SQL - which might consume fewer resources than a collection that's returned in one go.
But I'd have to question why you're doing this. You said "digging down for later batches takes considerably more time", which sounds like you're using a paging mechanism in your query, generating a row number and then picking out a range of 100 within that. If your client/application wants to get all the rows then it would be simpler to have a single query execution but fetch the result set in batches.
Unfortunately without any information about the application this is just speculation...
I studied this excellent paper on optimizing pagination:
http://www.inf.unideb.hu/~gabora/pagination/article/Gabor_Andras_pagination_article.pdf
I used technique 6 mainly. It describes how to limit query to fetch page x and onward. For added improvement, you can limit it further to fetch page x alone. If used right, it can bring a performance improvement by a factor of 1000.
Instead of returning custom table rows (which is very hard, if not impossible to interface with Java), I eneded up opening a sys_refcursor in my pl/sql which can be interfaced such as:
OracleCallableStatement stmt = (OracleCallableStatement) connection.prepareCall(sql);
stmt.registerOutParameter(someIndex, OracleTypes.CURSOR);
stmt.execute();
resultSet = stmt.getCursor(idx);

How to get refcursor result/output to show as text?

I'm trying to call a stored procedure in Oracle and show the results of the call, the problem is that it crashes on the line FETCH v_cur into v_a; with error: ORA-06504: PL/SQL: Return types of Result Set variables or query do not match.
I guess the output of the query does not match v_a VARCHAR2(100), but I don't know what to put there instead. The stored procedure that's being called does a join of several tables and selects more than 20+ different columns belonging to different tables. So what I would want is to just view the output of the query without having to refer to each result column separately. How I would go and do this ?
I'm using SQL Navigator (not that important I guess).
DECLARE
v_cur SYS_REFCURSOR;
v_a VARCHAR2(100);
BEGIN
pkg_get_results.get_rows(v_cur,to_date('2012/04/12', 'yyyy/mm/dd'),to_date('2012/04/12', 'yyyy/mm/dd'), '','','','');
LOOP
FETCH v_cur into v_a; -- what to put here ?
EXIT WHEN v_cur%NOTFOUND;
dbms_output.put_line(v_a );
END LOOP;
CLOSE v_cur;
END;
SQL Navigator does have the ability to do this for you. How to do it exactly depends on your version of Navigator, and it's conceivable (though I don't know) some versions may not have it.
Instructions can be found in this thread: http://sqlnavigator.inside.quest.com/thread.jspa?threadID=2466
Incidentally, Toad also has this ability.

Calling Oracle package procedures which return ref cursors in straight PL/SQL

I've got an Oracle 10g database which is accessed from an ASP.NET application. Although I've used SQL Server heavily in many different aspects and Oracle for querying and reporting, this is my first time using Oracle as the OLTP database for an application.
The database-level procedures in the packages are typically of the form:
-- TYPE refcur IS REF CURSOR;
PROCEDURE get_some_stuff(o_cursor OUT refcur, p_param1 IN INTEGER, p_param2 IN INTEGER) IS
BEGIN
OPEN o_cursor FOR
SELECT whatever
FROM whatever
END
I assume these are done this way for the benefit of the ADO.NET layer able to use the cursor from the output param and it is my understanding that this is the acceptable best practice for calling Oracle procs from .NET.
In SQL Server, for example, we don't have explicit ref cursors, if a proc returns a result set (or several result sets), that's accessible as an output result set in both ADO.NET and SSMS, and you can simply test the SPs by doing EXEC spname param1, param2.
The problem I'm having is that I don't know how to call these directly in SQL in Toad, for example, to be able to test changes at the PL/SQL level first before going to the app. I'm very used to being able to exercise and even re-mix stored procs and functions in SQL Server to be able to refactor the database interface layer without affecting the external interface to application-level code.
look at the link that OMG Ponies posted, but what you can do is
var x refcursor;
declare
PROCEDURE GET_SOME_STUFF(O_CURSOR OUT SYS_REFCURSOR, P_PARAM1 IN NUMBER, P_PARAM2 IN NUMBER) IS
BEGIN
OPEN O_CURSOR FOR
SELECT LEVEL, p_param1 ,P_PARAM2 FROM DUAL CONNECT BY LEVEL < 3;
END ;
BEGIN
GET_SOME_STUFF(:x , 5, 10);
END;
/
PRINT X;
you pretty much just wrap it in a anonymous block ad it will run. I use SQL Developer (highly recommmend, free with plenty of support) or SQL plus so I cannot help with TOAD, but I would expect it to be the same. In SQL Developer (and in SQL Navigator if memory serves correct) you can simply right click the package/method you wish and it will create the script for you.
in toad and navigator I believe you may be able to get the ref cursor in a pretty grid while in developer you get it in text.
SQL Developer you can unit test as well
Try this:
DECLARE
aCursor SYS_REFCURSOR;
someVariable SOME_TYPE;
FUNCTION SOME_FUNC_RETURNING_A_CURSOR RETURN SYS_REFCURSOR IS
csrLocal SYS_REFCURSOR;
BEGIN
OPEN csrLocal FOR SELECT whatever FROM wherever;
RETURN csrLocal;
END SOME_FUNC_RETURNING_A_CURSOR;
BEGIN
aCursor := SOME_FUNC_RETURNING_A_CURSOR;
WHILE TRUE LOOP
FETCH aCursor INTO someVariable;
EXIT WHEN aCursor%NOTFOUND;
...do whatever with variables...
END LOOP;
COMMIT;
END;
Share and enjoy.
I found an easier way to this ...try it (This will also generate script for you)
In the Procedure Editor, load your procedure. Click on the lightning
bolt to execute and you will see the Set Parameters window, which is
also available via the button on the Proc Editor toolbar that has an
image similar to (...) on it, next to the lightning bolt. Click on the
output options button and you'll see your options. If this is a weak ref
cursor then you must use the in-memory grid option. Results go to the
cursor results tab at the bottom of the PE after you execute.
http://toad.10940.n7.nabble.com/display-ref-cursor-in-toad-td1427.html

Resources