Page 258
that in the package header, you only specify a declaration including the subprogram name, its parameters (if any), and return type (if any). Also notice how the subprograms must follow all other declarations.
Those familiar with C will recognize this kind of function prototyping. Now that the specification exists, you're free to develop the actual code for these routines and change it as necessarywithout disturbing the specification. Developers can begin working on their applications as soon as the specifications are available, even if the body isn't written yet. Because of this separation of specification and implementation, programs using these routines need not be recompiled just because the code was enhanced. Dependent modules only require recompilation if the specification portion changes.
NOTE |
The standard practice for packages is to put the header and body in separate files. This way, if changes to just the code occur (which is more likely when a package is somewhat new yet still in a state of flux), just the body will need to be recompiled. |
The package body contains the actual code for subprograms exposed in the specification, as well as privately defined subprograms, variables, user-defined data types, and cursors. These objects are hidden from the rest of the world. The body may also contain initialization code, which is only executed once, the first time the package is referenced. While this may appear to have limited usefulness, I've used it to enable output, set various application flags (like Debug On/Off), and gather global user-related data. While the package body is a distinct object from the package header, the body cannot be successfully compiled until the header has been first compiled.
Listing 11.13 implements the package specification above.
Listing 11.13 libpkg.sqlBody for a Proposed Library Package
CREATE OR REPLACE PACKAGE BODY lib IS -- private global user defined types -- private global constants -- private global cursors -- private global variables debug_flag BOOLEAN; user_name VARCHAR(30); -- application user's login ID appl_name VARCHAR2(30); -- application currently running modl_name VARCHAR2(30); -- module (within app) currently running context_pt VARCHAR2(30); -- check-in point within module run_date DATE; -- when an application was launched -- private global subprograms -- implementation of exposed subprograms -- convert a BOOLEAN to a STRING (`TRUE', `FALSE', `NULL') FUNCTION bool_to_char(Pbool IN BOOLEAN) RETURN VARCHAR2 IS str VARCHAR2(5); -- capture string to return
Page 259
BEGIN IF (Pbool) THEN -- test Boolean value for TRUE str := `TRUE'; ELSIF (NOT Pbool) THEN -- FALSE str := `FALSE'; ELSE -- must be NULL str := `NULL'; END IF; -- test Boolean value RETURN (str); END bool_to_char; -- convert a STRING to BOOLEAN (TRUE, FALSE, NULL) FUNCTION char_to_bool(Pstr IN VARCHAR2) RETURN BOOLEAN IS Lstr VARCHAR2(32767); -- max string length Lbool BOOLEAN := NULL; -- local Boolean value (default) BEGIN Lstr := UPPER(LTRIM(RTRIM(Pstr))); -- remove leading/trailing spaces, Âuppercase IF (Lstr = `TRUE') THEN Lbool := TRUE; ELSIF (Lstr = `FALSE') THEN Lbool := FALSE; END IF; RETURN(Lbool); END char_to_bool; -- safe STRING to NUMBER conversion; returns NUMBER or NULL FUNCTION char_to_number(Pstr IN VARCHAR2, Pformat IN VARCHAR2 DEFAULT NULL) RETURN NUMBER IS BEGIN IF Pformat IS NULL THEN -- optional format not supplied RETURN (TO_NUMBER(Pstr)); ELSE RETURN (TO_NUMBER(Pstr, Pformat)); -- format supplied END IF; -- test for optional format EXCEPTION WHEN OTHERS THEN -- unknown value RETURN (NULL); END char_to_number; -- debug flag interface routines PROCEDURE debug_on IS -- turns debug on BEGIN debug_flag := TRUE; END debug_on; PROCEDURE debug_off IS -- turns debug off BEGIN debug_flag := FALSE; END debug_off; PROCEDURE debug_toggle IS -- toggles debug on/off BEGIN debug_flag := XOR(debug_flag, TRUE); END debug_toggle;
Page 260
Listing 11.13 Continued
FUNCTION debug_status -- is debug on/off? RETURN BOOLEAN IS BEGIN RETURN(debug_flag); END debug_status; BEGIN -- optional package initialization DBMS_OUTPUT.enable(1000000); debug_flag := FALSE; -- reset END lib; /
This object is called the package body in the declaration. The routines are declared just like they would be in an anonymous block. Also take note that the package body name must be the same as the package header name. Every routine declared in the header must have a corresponding implementation in the package body.
Another thing to watch out for is initializing variables in their declarations. Such initialization is performed only once, when the package is first referenced. This is because these variables are allocated and initialized exactly once, and then persist for the duration of the session. I could have reset the debug flag in its declaration and saved a couple of microseconds, but it makes it more explicit and maintainable to do it in the package initialization.
Any objects declared in the package specification are global in scope. That is, within the present session, any PL/SQL programs can reference and make use of any variables, cursors and subprograms defined in the specification. In contrast, variables, cursors, and subprograms that are declared only in the package body, while being global to the body itself, are hidden from the outside world. In either case, variables and cursors are persistent. They will continue to hold their present values until the package is aged out of memory or the session is terminated.
My recommendation for global variables is that you use them sparingly, if at all. Also, if you hide them in the body, you gain all of the benefits of persistence while controlling programmatic access to them. You handle this access by creating globally available subprograms to manipulate them.
The debug interface routines illustrate this standard practice for exposing not a variable itself to the world, but subroutines for manipulating the variable. For example, you don't want the developer modifying and checking the value of the debug_flag variable directly. If the debug flag were public and global, any program could change it, with potentially disastrous or at least undesirable results. If, in the future, something needs to be added or changed to the way the Debug feature is implemented, the details are hidden from the developers. This is a common approach in object-oriented programming.