Now let's see how we can take advantage of packaged cursors. First of all, you do not need to learn any new syntax to open, fetch from, and close packaged cursors; you just have to remember to prepend the package name to the name of the cursor. So if I want to get information about all the books having to do with PL/SQL, I can write a block like this:
DECLARE onebook book_info.bytitle_cur%ROWTYPE;BEGIN OPEN book_info.bytitle_cur ('%PL/SQL%'); LOOP EXIT WHEN book_info.bytitle_cur%NOTFOUND; FETCH book_info.bytitle_cur INTO onebook; book_info.display (onebook); END LOOP; CLOSE book_info.bytitle_cur;END;As you can see, I can %ROWTYPE a packaged cursor and check its attributes just as I would with a locally defined explicit cursor. Nothing new there!
There are some hidden issues lurking in this code, however. Because my cursor is declared in a package specification, its scope is not bound to any given PL/SQL block. Suppose that I run this code:
BEGIN -- Only open... OPEN book_info.bytitle_cur ('%PEACE%');END;and then, in the same session, I run the anonymous block with the LOOP shown above. I will then get this error:
ORA-06511: PL/SQL: cursor already openThis happened because in my "only open" block, I neglected to close the cursor. Even though the block terminated, my packaged cursor did not close.
Given the persistence of packaged cursors, you should always keep the following rules in mind:
· Never assume that a packaged cursor is closed (and ready to be opened).
· Never assume that a packaged cursor is opened (and ready to be closed).
· Always be sure to explicitly close your packaged cursor when you are done with it.
|
If you neglect these rules, you might well execute an application that makes certain assumptions, and then pays the price in unexpected and unhandled exceptions. So the question then becomes: how best can you remember and follow these rules? My suggestion is to buildprocedures that perform the open and close operations for you—and take all these nuances and possibilities into account.
The following package offers an example of this technique.
/* File on web: openclose.sql */CREATE OR REPLACE PACKAGE personnelIS CURSOR emps_for_dept (deptno_in IN employee.department_id%TYPE) IS SELECT * FROM employee WHERE department_id = deptno_in; PROCEDURE open_emps_for_dept(deptno_in IN employee.department_id%TYPE, close_if_open IN BOOLEAN:= TRUE); PROCEDURE close_emps_for_dept; END personnel;I have a packaged cursor along with procedures to open and close the cursor. So if I want to loop through all the rows in the cursor, I would write code like this:
DECLARE one_emp personnel.emps_for_dept%ROWTYPE;BEGIN personnel.open_emps_for_dept (1055); LOOP EXIT WHEN personnel.emps_for_dept%NOTFOUND; FETCH personnel.emps_for_dept INTO one_emp;... END LOOP; personnel.close_emps_for_dept;END;I don't use explicit OPEN and CLOSE statements; instead, I call the corresponding procedures, which handle complexities related to packaged cursor persistence. I urge you to examine the openclose.sql file available from the O'Reilly site to study the implementation of these procedures.
You have a lot to gain by creating cursors in packages and making those cursors available to the developers on a project. Crafting precisely the data structures you need for your application is hard and careful work. These same structures—and the data in them—are used in your PL/SQL programs, almost always through the use of a cursor. If you do not package up your cursors and provide them "free of charge and effort" to all developers, each of them will write his or her own variations of these cursors, leading to all sorts of performance and maintenance issues. Packaging cursors is just one example of using packages to encapsulate access to data structures, which is explored further in Section 17.5.
|