Here's a quick demonstration of the issue the developer was encountering.
Take a look at the following package specification and body.
SQL> CREATE OR REPLACE PACKAGE testpkg AS 2 PROCEDURE proc_main(integer_in IN PLS_INTEGER); 3 END testpkg; 4 / Package created. SQL> CREATE OR REPLACE PACKAGE BODY testpkg AS 2 PROCEDURE proc1_calls_proc2(integer_in IN PLS_INTEGER) IS 3 BEGIN 4 IF integer_in = 0 THEN 5 DBMS_OUTPUT.PUT_LINE('Done!'); 6 ELSE 7 DBMS_OUTPUT.PUT_LINE('integer_in = '||integer_in); 8 proc2_calls_proc1(integer_in - 1); 9 END IF; 10 END proc1_calls_proc2; 11 12 PROCEDURE proc2_calls_proc1(integer_in IN PLS_INTEGER) IS 13 BEGIN 14 IF integer_in = 0 THEN 15 DBMS_OUTPUT.PUT_LINE('Done!'); 16 ELSE 17 DBMS_OUTPUT.PUT_LINE('integer_in = '||integer_in); 18 proc1_calls_proc2(integer_in - 1); 19 END IF; 20 END proc2_calls_proc1; 21 22 PROCEDURE proc_main(integer_in IN PLS_INTEGER) IS 23 BEGIN 24 proc1_calls_proc2(integer_in); 25 END proc_main; 26 END testpkg; 27 / Warning: Package Body created with compilation errors. SQL> SHOW ERRORS Errors for PACKAGE BODY TESTPKG: LINE/COL ERROR -------- ----------------------------------------------------------------- 8/7 PL/SQL: Statement ignored 8/7 PLS-00313: 'PROC2_CALLS_PROC1' not declared in this scope SQL>
As you can see, we encounter an error at line 8 in the package body because the procedure proc1_calls_proc2 contains a reference to proc2_calls_proc1 but proc2_calls_proc1 has not been declared above it. We could move proc2_calls_proc1 above proc1_calls_proc2 but we'd encounter the same problem because both procedures reference each other. Like the developer had done we could declare proc2_calls_proc1 in the package specification but then we'd be exposing a private procedure to the public and we want to avoid that.
The solution to this is that you can pre-declare any of your procedures/functions in a package body at the top of the package body. This way the definitions for all of your methods are available to all other methods throughout the package body. The cases for doing this are few and far between but it is handy to know when you do need it. Have a look at line 2 of the package body below. By adding the definition for proc2_calls_proc1 above proc1_calls_proc2 we are able successfully compile the package.
SQL> CREATE OR REPLACE PACKAGE testpkg AS 2 PROCEDURE proc_main(integer_in IN PLS_INTEGER); 3 END testpkg; 4 / Package created. SQL> CREATE OR REPLACE PACKAGE BODY testpkg AS 2 PROCEDURE proc2_calls_proc1(integer_in IN PLS_INTEGER); 3 4 PROCEDURE proc1_calls_proc2(integer_in IN PLS_INTEGER) IS 5 BEGIN 6 IF integer_in = 0 THEN 7 DBMS_OUTPUT.PUT_LINE('Done!'); 8 ELSE 9 DBMS_OUTPUT.PUT_LINE('P1: integer_in = '||integer_in); 10 proc2_calls_proc1(integer_in - 1); 11 END IF; 12 END proc1_calls_proc2; 13 14 PROCEDURE proc2_calls_proc1(integer_in IN PLS_INTEGER) IS 15 BEGIN 16 IF integer_in = 0 THEN 17 DBMS_OUTPUT.PUT_LINE('Done!'); 18 ELSE 19 DBMS_OUTPUT.PUT_LINE('P2: integer_in = '||integer_in); 20 proc1_calls_proc2(integer_in - 1); 21 END IF; 22 END proc2_calls_proc1; 23 24 PROCEDURE proc_main(integer_in IN PLS_INTEGER) IS 25 BEGIN 26 proc1_calls_proc2(integer_in); 27 END proc_main; 28 END testpkg; 29 / Package body created. SQL> exec testpkg.proc_main(5); P1: integer_in = 5 P2: integer_in = 4 P1: integer_in = 3 P2: integer_in = 2 P1: integer_in = 1 Done! PL/SQL procedure successfully completed.
I prefer not to do this unless absolutely necessary. If find that my code becomes much more organized and readable if I'm forced to define procedures and functions in the correct order in a package body. Where this does prove useful is in the case above and also when you've inherited code where packages are massive, poorly written, and contain hundreds of small procedures. In these instances, when you need to make a change to existing code or add a new procedure it can be a royal pain to figure out all of the inter-procedure/function dependencies. Just declare the procedures at the top of the package and you've got a workaround until you can reorganize the code entirely.
No comments:
Post a Comment