A package is a collection of pl/sql elements that are packaged or grouped together within a special begin-end syntax, a kind of meta-block. here is a partial list of the kind elements you can place in a package:*cursor;*variables(scalars,records,tables,ect) and constants;*exception names and pragra,s for associating an error number with an exception;*pl/sql table and record type statements;*procedure and functions. Pakages provide a st4ucture to organzie your modules and other pl/sql elements.When you place a priogarm unit in a package you automatically create a "context" for that program. The pl/sql package is a deceptively simple,yet powerful construct. It consists of up to two distinct parts;the specification and the body.
# The package specification,which defines the public interface of the package;those elements taht can be referenced outside the package.
#The package body,which containes the implementation of the package and elements of the package you want to keep hidden from view.
Alythough any pl/sql package must have the same structure and follow the same rules, these are different types of packages that will play different roles in your application.
The lowest layer of code upon which you build your pl/sql application is the sql language.Pl/sql was designed explicitly as a procedureal language extentsion to sql;most of your pl/sql programs will function as an ionterface between a user of some kind and the database.
The next layer of code is the core pl/sql language.At the center of the pl/sql universe, we have the STANDARD and DBMS_STANDARD packages,whcich define the basic element of the language. When you execute the to_char() function or even use the like operator,you are actuallly calling elements of the standard package. Since these two packages are the defualt in pl/sql, you do not have to explicitely reference the package name.
When an object in a package is referenced for the first time, the entire package ( already compiled abd validated) is loaded into memory(the system global area of the rdms). All other package elements are thereby made immediately avaible for future calls to the packages. The pl/sql runtime engine does not have to keep retrieving program elements or data from disk each time a new object is referenced. To use a package ,you must know what is defined and avaible inside the package. This information is contained in the package specification.
The package specification containes the definition or specification of all elements in the package that may be referenced outside of the package. These are called the public elements of the package. The demo of package specification:
PACKAGE pets_r_us
IS
max_pets_in_facility CONSTANT INTEGER := 120;
pet_is_sick EXCEPTION;
next_appointment DATE := SYSDATE;
CURSOR onepet_cur (pet_id_in IN INTEGER) RETURN pet%ROWTYPE;
CURSOR allpets_cur IS SELECT pet_id, name, owner FROM pet;
FUNCTION next_pet_shots (pet_id_in IN NUMBER) RETURN DATE;
PROCEDURE set_schedule (pet_id_in IN NUMBER);
END pets_r_us;
A package owns its elements, just as a table owns its columns. An individual element of a package only makes sense, in fact, in the context of the package. Consequently, you use the same dot notation employed in "table.column" syntax for "package.element".
There is one exception to the rule of qualifying a package element with its package name. Inside the body of a package, you do not need to qualify references to other elements of that package. PL/SQL will automatically resolve your reference within the scope of the package; the package is the "current" context.
To use packages most effectively, you must understand the architecture of these constructs within an Oracle Server instance.keep these basic principles in mind:
The compiled code for stored objects (procedures, functions, and packages) is shared by all users of the instance with execute authority on that code.
Each Oracle session has its own copy of the in-memory data defined within stored objects.
The Oracle Server applies a least-recently used (LRU) algorithm to maintaining compiled code in shared memory.
When a user executes a stored program or references a package-based data structure, the PL/SQL runtime engine first must make sure that the compiled version of that code is available in the System Global Area or SGA of the Oracle instance. If the code is present in the SGA, it is then executed for that user. If the code is not present, the Oracle Server reads the compiled code from disk and loads it into the shared memory area. At that point the code is available to all users with execute authority on that code.
So if session 1 is the first account in the Oracle instance to reference package A, session 1 will cause the compiled code for A to be loaded into shared memory. When session 2 references an element in package A, that code is already present in shared memory and is re-used.
When a package is loaded into shared memory, a contiguous amount of memory is required to hold the package (the same is true for any piece of stored code). So if you have a large package, you may have to tune your shared pool in the SGA to accommodate this package. (The shared pool is the area in the SGA devoted to shared SQL and PL/SQL statements.) You can get more space for your stored code by increasing the value of the SHARED_POOL_SIZE parameter in the database instance initialization file.
The Oracle Server uses a least-recently used (LRU) algorithm to decide which items in the shared pool will remain present. If your package is flushed out of memory and is then needed by another program, the compiled code of the package will have to be read again from disk. Contiguous memory will also need to be available at that point.
If you know that you will want to use a large package or standalone program intermittently throughout application execution and do not want to have the code flushed out of memory, you can use the DBMS_SHARED_POOL package to pin your code into memory. The KEEP procedure of this package exempts the specified program or package from the LRU algorithm.
To pin the config package into shared memory, for example, you would execute this statement:
DBMS_SHARED_POOL.KEEP ('config');
You can also unpin a program with the UNKEEP program. The DBMS_SHARED_POOL package is not installed by default when you create an Oracle Server instance. You will need to execute (usually from within the SYS account) the dbmspool.sql script in the admin subdirectory of your particular version of the server. For example, on Windows95 and Oracle 7.2, you would issue this command in SQL*Plus:
SQL> @c:\orawin95\rdbms72\admin\dbmspool
You should only pin programs if absolutely necessary and unavoidable (you cannot, for instance, further expand the overall size of the SGA and the shared pool). Why? In answer, I quote from the above-mentioned dbmspool.sql file about KEEP:
--WARNING: This procedure may not be supported in the future when
--and if automatic mechanisms are implemented to make this unnecessary.
You can calculate the size of a package or any other piece of stored code by executing queries against the USER_OBJECT_SIZE data dictionary view. This view contains information about the size of the source code, the size of the parsed code, and the size of the compiled code. The SQL statement below will display the names and sizes for any stored code larger than the specified SQL*Plus parameter:
SELECT name, type, source_size, parsed_size, code_size
FROM user_object_size
WHERE code_size > &1
ORDER BY code_size DESC
/
A user's relationship to data structures defined in stored code, particularly package data, is very different from that of the compiled code. While the same compiled code is shared, each user gets her own version of the data. This process is clear for procedures and functions. Any data declared in the declaration section of these programs is instantiated, manipulated, and then, on the termination of that program, erased. Every time a user calls that procedure or function, she gets her own local versions of the data.
The situation with packages is the same as that with stored code, but is less obvious at first glance. Data declared at the package level (defined outside of any particular procedure or function in the package) persist for as long as a session is active—but those data are specific to a single Oracle session or connection. Each Oracle session is assigned its own private PL/SQL area, which contains a copy of the package data. This private PL/SQL area is maintained by the PL/SQL runtime engine for as long as your session is running. When session 1 references package A, session 1 instantiates her own version of the data structures used by A. When session 2 calls a program in A or accesses a data structure defined by A, session 2 gets her own copy of that data. Any changes made to the memory-based package data in session 1 is not affected by and does not affect the data in session 2.
One of the most valuable aspects of a package is its ability to truly enforce information hiding. With a package you can not only modularize your secrets behind a procedural interface, you can keep those parts of your application completely private.
An element of a package, whether it is a variable or a module, can be either public or private:
Public
An element is public if it is defined in the specification. A public element can be referenced directly from other programs and PL/SQL blocks. The package specification is, in a sense, the gatekeeper for the package. It determines the package elements to which a developer may have access.
Private
An element is private if it is defined only in the body of the package, but does not appear in the specification. A private element cannot be referenced outside of the package. Any other element of the package may, however, reference and use a private element.
Whether a variable is declared in the specification or body, it does function as a global piece of data. Once the package is instantiated in your session, data declared in the package persist for the duration of the session. A variable will retain its value until it is changed. That value will be available to any program that has access to the data. The kind of access depends on whether the variable is defined in the package specification or in the body.
To understand the consequences of public (specification-declared) data and private (body-declared) data in packages, consider the following simple package. In downsize, hire_date is a public variable and fire_date is a private variable.
PACKAGE downsize
IS
v_hire_date DATE;
END;
PACKAGE BODY downsize
IS
v_fire_date DATE;
END;
Since v_hire_date is defined in the package specification, I can directly reference that variable in my own code outside of the downsize package, as follows:
Read the value of the hire_date variable:
last_hired := downsize.v_hire_date;
Change the value of the hire_date variable to ten days in the future:
downsize.v_hire_date := SYSDATE + 10;
If I try to access v_fire_date in the same way, however, my code will not compile. It is hidden behind the public boundary of the package. Its value is maintained in my private global area since it is in a package, but the only programs that can reference it are those defined within the package itself, either in the body or the specification.
When you first create a package, your decision about which elements of a package are public and which private is not cast in stone. You can, in fact, switch a public element to private and vice versa at any time.
If you find that a private element program or cursor should instead be made public, simply add the header of that element to the package specification and recompile. It will then be visible outside of the package. Notice that you do not need to make any changes at all to the package body.
If you want to make a private variable accessible directly from outside the package, you will need to remove the declaration of that data structure from the body and paste it into the specification. You cannot declare the same element in both the body and the specification.
If you do make a public element private, you will need to remember that any program that referenced that element will no longer compile successfully.
The general syntax for the package body is shown below:
PACKAGE BODY package_name
IS
[ declarations of variables and types ]
[ header and SELECT statement of cursors ]
[ header and body of modules ]
[ BEGIN
executable statements ]
[ EXCEPTION
exception handlers ]
END [ package_name ];
In the body you can declare other variables, but you do not repeat the declarations in the specification. The body contains the full implementation of cursors and modules. In the case of a cursor, the package body contains both the header and the SQL statement for the cursor. In the case of a module, the package body contains both the header and body of the module.
The BEGIN keyword indicates the presence of an execution or initialization section for the package. This section can also optionally include an exception section.
As with a procedure, function, and package specification, you can add the name of the package, as a label, after the END keyword in both the specification and package.
Packages are the most important construct in PL/SQL for building reusable code and plug-and-play components, and for employing object-oriented design techniques. As you become more comfortable with the language, you will find more of your time spent inside packages—and using programs from packages built by other developers. This may be a very pleasant and rewarding experience—if the packages are designed and implemented properly. If, on the other hand, you decide to build your packages in the same helter-skelter method (or lack thereof) I run into way too often, life out there in front of the monitor may get pretty miserable.
If you start with a package, several benefits accrue:
You immediately work at a higher level of abstraction. You think of your single program as just one component of a whole range of related functionality. In the first implementation of the package you may not be aware of even one other program for this package, but you are allowing for the possibility of such programs.
Related to the level of abstraction, you find yourself creating separate layers and partitions of functionality. Then, as you work on additional programs, you identify the appropriate package for that program. Lo and behold, you find things falling into place. You realize that everything has a place. Your code takes on an elegant sense of internal organization that makes it easier to use and understand.
From the beginning, all calls to that program employ the dot notation necessary (and, I would argue, inevitable) to reference a package-based element. You don't have to go back later and change those calls or create another layer of code to support backward compatibility.
A package is a collection of PL/SQL elements, including data structures (from cursors to constants) and program units (procedures and functions). Packages are generally the most complicated and extended pieces of code PL/SQL developers will write. To make matters worse, the current array of PL/SQL development environments do not offer any tools for viewing and managing a package as a collection. A package is treated and presented no differently from a single function—just a whole bunch of lines of source code.
As a result, it is up to you to design and write your package to make it as readable and maintainable as possible. There are two fundamental strategies you can employ to help meet this objective:
Strategy 1: Use all available techniques to make your code as clean, modular, and structured as possible.
Strategy 2: Come up with a consistent coding style and format for your packages—and get people to follow that style.
As with the declaration sections of procedures and functions, you must (both in the package specification and body) declare all variables and data structures before you declare any program units. But what about the order of these program units themselves? As you can see from my banners, I always try to define all my private modules before any of my public modules. These are the building blocks used by the public programs. I group them together so they are easier to locate.
Is this ordering strictly necessary? Yes and no. Yes, you must define a private program before it is referenced by another program in the package (public or private). No, you do not have to group them together. You could instead define all private modules just before they are used by their public counterparts. This can make sense if the private program is only used by a single public program. If it is shared by many public programs (or other private ones, for that matter), then this placement does not accurately reflect its role in the package.
You can, by the way, place the definitions of the public program units anywhere in the package body (after the variable declarations)—even after they are referenced by another program. How is this possible? Since their headers have already been established in the package specification, the PL/SQL compiler has all the information it needs to resolve the reference.
As a rule, developers are much too careless about the names they give to their packages and the elements inside those packages, (most importantly, procedures and functions.) There are two aspects to coming up with the right names for your code elements:
The structure of the name should match the role that element plays in your code.
The name should reflect what the element does in your code.
Have you ever thought about the structure of the names you choose? PL/SQL is a computer language. It is much simpler than human languages like Japanese or English, but it still has many of the same grammatical components, such as nouns and verbs. RAISE_APPLICATION_ERROR, for example, contains a verb (RAISE)-noun (APPLICATION_ERROR) combination, as in: "Raise this error." The built-in function, SUBSTR, is an example of a noun (SUBSTR), as in: "if the substring is NULL, then ask for a dollar amount."
PL/SQL on the other hand, is more complicated than human languages because you, the developer, get to make up words in the language as you go. You define new nouns and verbs every time you declare a variable or define a new program. This means that each and every one of us is, at least in part, responsible for the integrity of the PL/SQL language. Keep this in mind as you name your program elements. Let's apply this consideration to packages.
First of all, the name of the package should always be structured as a noun. The package itself does not do anything, so it cannot and should not be an action verb. The package name declares, as simply as possible, the contents of the package.
Keep in mind that when you reference a package element outside of the package you must use dot notation (package.element). As a result, you will want to avoid redundancy in your package and element names.
Sure, we want our code to be flexible, in a practical sort of way. It is quite another thing to internalize this issue in the context of packages and figure out how to take full advantage of the package structure to offer maximum flexibility.
If a program is going to be widely reusable, it should be able to adapt to different circumstances to meet different needs. It is easy to talk about flexibility. I have found that when it comes to packages there are two basic ways to be flexible when writing programs for others to use:
Offer lots of parameters in the parameter lists of the package's functions and procedures. This is the traditional, well-worn path.
Provide toggles or on-off switches, distinct from the main programs of the package, which modify the behavior of those programs. This approach takes advantage of the package structure to offer a new way of doing things.
A toggle is a set of three programs: two procedures that allow you to turn a feature on or off, and a function to tell you the current status (on or off). The liberal application of toggles can transform the usability of your packages.
One of the most powerful aspects of the package is the ability to overload program units. When you overload, you define more than one program with the same name. These programs will differ in other ways (usually the number and types of parameters) so that at runtime the PL/SQL engine can figure out which of the programs to execute. You can take advantage of the overloading feature of packages to make your package-based features as accessible as possible.
Does overloading sound unfamiliar or strange? Well, have you ever used the TO_CHAR function? If so, then you have already been enjoying the creature comforts of overloading. TO_CHAR converts both numbers and dates to strings. Have you ever wondered why you don't have to call functions with names like TO_CHAR_FROM_DATE or TO_CHAR_FROM_NUMBER? Probably not. You probably just took TO_CHAR for granted, and that is how it should be.
In reality, there are two different TO_CHAR functions (both defined in the STANDARD package): one to convert dates and another to convert numbers. The reason that you don't have to care about such details and can simply execute TO_CHAR is that the PL/SQL runtime engine examines the kind of data you pass to TO_CHAR and then automatically figures out which of the two functions (with the same name) to execute. It's like magic, only it's better than magic: it's intelligent software!
To build packages that are both immediately useful and enhanceable over the long-run, you must avoid any kind of code duplication inside the package. You need to be ready, willing, and able to create private programs in your package to contain all the shared code behind the public programs of the package. The alternative is a debilitating reliance on the Windows cut-and-paste feature. Cut-and-paste will let you build rapidly—but what you will be building is a wide, deep hole from which you will never see the light of day.
You implement PL/SQL-based global data with package data. Package data is any data structure declared in a package body or specification. There are two kinds of package data: public data (declared in the specification) and private data (declared in the body).
What's the difference between public and private? Public global data is the proverbial "loose cannon" of programming. Public package data is certainly very convenient. Simply declare a few variables in a package specification and they are available from/to any module. If you need to get a piece of information, just grab it from the global. If you want to change the value of that variable, go at it. Reliance on global data structures, however, leads to two significant problems:
Loss of control. When you declare a data structure in the package specification, you lose control over that data structure's value. Since any program can write to it, you can never trust its value. Instead, you must trust developers to do the right thing when working with that variable. Now, I am as trusting as the next programmer, but anarchy really has little place in the world of software development.
Loss of flexibility. When you allow programmers to make direct references to global data, you lose the flexibility you need to enhance your application to take advantage of new features. Very specifically, you limit your ability to change the data structures used to implement your global data.
you never define variables in the specification of a package (except when explicitly needed that way, as discussed at the end of this section). Instead, you always declare the variable in the package body. You then provide a procedure to set the value of that variable and a function to retrieve the value of that variable.
There is one other case in which specification-based variables are useful: anchored declarations. You can anchor or base the declaration of a variable on another, predefined structure. To do this, you use the %TYPE and %ROWTYPE attributes. The most common way %TYPE is used is to anchor a local PL/SQL variable to a database column.
PL/Vision is a collection of PL/SQL packages and supporting SQL*Plus scripts that can change radically the way you develop applications with the PL/SQL language.
What can PL/Vision do for you? The possibilities are almost endless:
Improve your productivity. PL/Vision goes a long way towards helping you avoid reinventing the wheel. Need to change a long string into a word-wrapped paragraph? Use the PLVprs.wra procedure. Want to display the contents of a PL/SQL table? Call the PLVtab.display procedure. Need to log activity to the database or maybe even write your information to a PL/SQL table? Call the PLVlog.put_line program. By using PL/Vision, you write much less code yourself, and instead spend your time deciding which prebuilt components of PL/Vision to plug into your own applications. You are able to focus much more of your effort on implementing the business rules for your systems.
Decrease the number of bugs in your code and fix the bugs you do find more rapidly. Since you will be writing less code, you will minimize the opportunities for bugs to creep into your own programs. When you do have compile errors, you can call the PLVvu.err program to show you precisely where the error occurred in your program. Furthermore, PL/Vision packages offer many ways to trace the actions taken within those packages. When you need more information, you simply call the appropriate package toggle to turn on a trace and then run your test. When you are done testing and debugging, you turn off the trace mechanisms and put your application into production.
Help you develop and enforce coding standards and best practices. You will do this in two ways: first, by using packages that explicitly support coding standards, such as PLVgen; second, by examining the code behind PL/Vision. This PL/SQL library has provided numerous opportunities for me to put my own best practices for packages into action. You will, no doubt, find occasional violations of my best practices, but by and large the code in PL/Vision should provide a wealth of ideas and examples for your own development.
Increase the percentage of reusable code in your applications. The more you leverage PL/Vision, the fewer new programs you have to write yourself. And this advantage doesn't just accrue to individual developers. You can use PL/Vision across multiple applications—it can be part of a truly enterprise-wide object and module library.
Demonstrate how to modularize and build layers. I don't want you to simply use PL/Vision. I want you to learn how and why I built PL/Vision so that you can accomplish the same kind of development yourself. We all need to be fanatically devoted to modularizing code for maximum reusability. We all need to become sensitive to identifying program functionality that should be broken out into different layers. To some extent, you can develop such sensitivity only by practicing the craft of software construction. But you can also examine closely the work of others and learn from their example (both the good and the bad).
Inspire you to be creative, to take risks in your coding. I have found that the real joy of programming is to be found in trying out new ways of doing things. When you stretch boundaries—whether they are the boundaries of your own experience or those of the documented features of a language—you make discoveries. And when those discoveries turn out to be productive, you create new things.
# The package specification,which defines the public interface of the package;those elements taht can be referenced outside the package.
#The package body,which containes the implementation of the package and elements of the package you want to keep hidden from view.
Alythough any pl/sql package must have the same structure and follow the same rules, these are different types of packages that will play different roles in your application.
The lowest layer of code upon which you build your pl/sql application is the sql language.Pl/sql was designed explicitly as a procedureal language extentsion to sql;most of your pl/sql programs will function as an ionterface between a user of some kind and the database.
The next layer of code is the core pl/sql language.At the center of the pl/sql universe, we have the STANDARD and DBMS_STANDARD packages,whcich define the basic element of the language. When you execute the to_char() function or even use the like operator,you are actuallly calling elements of the standard package. Since these two packages are the defualt in pl/sql, you do not have to explicitely reference the package name.
When an object in a package is referenced for the first time, the entire package ( already compiled abd validated) is loaded into memory(the system global area of the rdms). All other package elements are thereby made immediately avaible for future calls to the packages. The pl/sql runtime engine does not have to keep retrieving program elements or data from disk each time a new object is referenced. To use a package ,you must know what is defined and avaible inside the package. This information is contained in the package specification.
The package specification containes the definition or specification of all elements in the package that may be referenced outside of the package. These are called the public elements of the package. The demo of package specification:
PACKAGE pets_r_us
IS
max_pets_in_facility CONSTANT INTEGER := 120;
pet_is_sick EXCEPTION;
next_appointment DATE := SYSDATE;
CURSOR onepet_cur (pet_id_in IN INTEGER) RETURN pet%ROWTYPE;
CURSOR allpets_cur IS SELECT pet_id, name, owner FROM pet;
FUNCTION next_pet_shots (pet_id_in IN NUMBER) RETURN DATE;
PROCEDURE set_schedule (pet_id_in IN NUMBER);
END pets_r_us;
A package owns its elements, just as a table owns its columns. An individual element of a package only makes sense, in fact, in the context of the package. Consequently, you use the same dot notation employed in "table.column" syntax for "package.element".
There is one exception to the rule of qualifying a package element with its package name. Inside the body of a package, you do not need to qualify references to other elements of that package. PL/SQL will automatically resolve your reference within the scope of the package; the package is the "current" context.
To use packages most effectively, you must understand the architecture of these constructs within an Oracle Server instance.keep these basic principles in mind:
The compiled code for stored objects (procedures, functions, and packages) is shared by all users of the instance with execute authority on that code.
Each Oracle session has its own copy of the in-memory data defined within stored objects.
The Oracle Server applies a least-recently used (LRU) algorithm to maintaining compiled code in shared memory.
When a user executes a stored program or references a package-based data structure, the PL/SQL runtime engine first must make sure that the compiled version of that code is available in the System Global Area or SGA of the Oracle instance. If the code is present in the SGA, it is then executed for that user. If the code is not present, the Oracle Server reads the compiled code from disk and loads it into the shared memory area. At that point the code is available to all users with execute authority on that code.
So if session 1 is the first account in the Oracle instance to reference package A, session 1 will cause the compiled code for A to be loaded into shared memory. When session 2 references an element in package A, that code is already present in shared memory and is re-used.
When a package is loaded into shared memory, a contiguous amount of memory is required to hold the package (the same is true for any piece of stored code). So if you have a large package, you may have to tune your shared pool in the SGA to accommodate this package. (The shared pool is the area in the SGA devoted to shared SQL and PL/SQL statements.) You can get more space for your stored code by increasing the value of the SHARED_POOL_SIZE parameter in the database instance initialization file.
The Oracle Server uses a least-recently used (LRU) algorithm to decide which items in the shared pool will remain present. If your package is flushed out of memory and is then needed by another program, the compiled code of the package will have to be read again from disk. Contiguous memory will also need to be available at that point.
If you know that you will want to use a large package or standalone program intermittently throughout application execution and do not want to have the code flushed out of memory, you can use the DBMS_SHARED_POOL package to pin your code into memory. The KEEP procedure of this package exempts the specified program or package from the LRU algorithm.
To pin the config package into shared memory, for example, you would execute this statement:
DBMS_SHARED_POOL.KEEP ('config');
You can also unpin a program with the UNKEEP program. The DBMS_SHARED_POOL package is not installed by default when you create an Oracle Server instance. You will need to execute (usually from within the SYS account) the dbmspool.sql script in the admin subdirectory of your particular version of the server. For example, on Windows95 and Oracle 7.2, you would issue this command in SQL*Plus:
SQL> @c:\orawin95\rdbms72\admin\dbmspool
You should only pin programs if absolutely necessary and unavoidable (you cannot, for instance, further expand the overall size of the SGA and the shared pool). Why? In answer, I quote from the above-mentioned dbmspool.sql file about KEEP:
--WARNING: This procedure may not be supported in the future when
--and if automatic mechanisms are implemented to make this unnecessary.
You can calculate the size of a package or any other piece of stored code by executing queries against the USER_OBJECT_SIZE data dictionary view. This view contains information about the size of the source code, the size of the parsed code, and the size of the compiled code. The SQL statement below will display the names and sizes for any stored code larger than the specified SQL*Plus parameter:
SELECT name, type, source_size, parsed_size, code_size
FROM user_object_size
WHERE code_size > &1
ORDER BY code_size DESC
/
A user's relationship to data structures defined in stored code, particularly package data, is very different from that of the compiled code. While the same compiled code is shared, each user gets her own version of the data. This process is clear for procedures and functions. Any data declared in the declaration section of these programs is instantiated, manipulated, and then, on the termination of that program, erased. Every time a user calls that procedure or function, she gets her own local versions of the data.
The situation with packages is the same as that with stored code, but is less obvious at first glance. Data declared at the package level (defined outside of any particular procedure or function in the package) persist for as long as a session is active—but those data are specific to a single Oracle session or connection. Each Oracle session is assigned its own private PL/SQL area, which contains a copy of the package data. This private PL/SQL area is maintained by the PL/SQL runtime engine for as long as your session is running. When session 1 references package A, session 1 instantiates her own version of the data structures used by A. When session 2 calls a program in A or accesses a data structure defined by A, session 2 gets her own copy of that data. Any changes made to the memory-based package data in session 1 is not affected by and does not affect the data in session 2.
One of the most valuable aspects of a package is its ability to truly enforce information hiding. With a package you can not only modularize your secrets behind a procedural interface, you can keep those parts of your application completely private.
An element of a package, whether it is a variable or a module, can be either public or private:
Public
An element is public if it is defined in the specification. A public element can be referenced directly from other programs and PL/SQL blocks. The package specification is, in a sense, the gatekeeper for the package. It determines the package elements to which a developer may have access.
Private
An element is private if it is defined only in the body of the package, but does not appear in the specification. A private element cannot be referenced outside of the package. Any other element of the package may, however, reference and use a private element.
Whether a variable is declared in the specification or body, it does function as a global piece of data. Once the package is instantiated in your session, data declared in the package persist for the duration of the session. A variable will retain its value until it is changed. That value will be available to any program that has access to the data. The kind of access depends on whether the variable is defined in the package specification or in the body.
To understand the consequences of public (specification-declared) data and private (body-declared) data in packages, consider the following simple package. In downsize, hire_date is a public variable and fire_date is a private variable.
PACKAGE downsize
IS
v_hire_date DATE;
END;
PACKAGE BODY downsize
IS
v_fire_date DATE;
END;
Since v_hire_date is defined in the package specification, I can directly reference that variable in my own code outside of the downsize package, as follows:
Read the value of the hire_date variable:
last_hired := downsize.v_hire_date;
Change the value of the hire_date variable to ten days in the future:
downsize.v_hire_date := SYSDATE + 10;
If I try to access v_fire_date in the same way, however, my code will not compile. It is hidden behind the public boundary of the package. Its value is maintained in my private global area since it is in a package, but the only programs that can reference it are those defined within the package itself, either in the body or the specification.
When you first create a package, your decision about which elements of a package are public and which private is not cast in stone. You can, in fact, switch a public element to private and vice versa at any time.
If you find that a private element program or cursor should instead be made public, simply add the header of that element to the package specification and recompile. It will then be visible outside of the package. Notice that you do not need to make any changes at all to the package body.
If you want to make a private variable accessible directly from outside the package, you will need to remove the declaration of that data structure from the body and paste it into the specification. You cannot declare the same element in both the body and the specification.
If you do make a public element private, you will need to remember that any program that referenced that element will no longer compile successfully.
The general syntax for the package body is shown below:
PACKAGE BODY package_name
IS
[ declarations of variables and types ]
[ header and SELECT statement of cursors ]
[ header and body of modules ]
[ BEGIN
executable statements ]
[ EXCEPTION
exception handlers ]
END [ package_name ];
In the body you can declare other variables, but you do not repeat the declarations in the specification. The body contains the full implementation of cursors and modules. In the case of a cursor, the package body contains both the header and the SQL statement for the cursor. In the case of a module, the package body contains both the header and body of the module.
The BEGIN keyword indicates the presence of an execution or initialization section for the package. This section can also optionally include an exception section.
As with a procedure, function, and package specification, you can add the name of the package, as a label, after the END keyword in both the specification and package.
Packages are the most important construct in PL/SQL for building reusable code and plug-and-play components, and for employing object-oriented design techniques. As you become more comfortable with the language, you will find more of your time spent inside packages—and using programs from packages built by other developers. This may be a very pleasant and rewarding experience—if the packages are designed and implemented properly. If, on the other hand, you decide to build your packages in the same helter-skelter method (or lack thereof) I run into way too often, life out there in front of the monitor may get pretty miserable.
If you start with a package, several benefits accrue:
You immediately work at a higher level of abstraction. You think of your single program as just one component of a whole range of related functionality. In the first implementation of the package you may not be aware of even one other program for this package, but you are allowing for the possibility of such programs.
Related to the level of abstraction, you find yourself creating separate layers and partitions of functionality. Then, as you work on additional programs, you identify the appropriate package for that program. Lo and behold, you find things falling into place. You realize that everything has a place. Your code takes on an elegant sense of internal organization that makes it easier to use and understand.
From the beginning, all calls to that program employ the dot notation necessary (and, I would argue, inevitable) to reference a package-based element. You don't have to go back later and change those calls or create another layer of code to support backward compatibility.
A package is a collection of PL/SQL elements, including data structures (from cursors to constants) and program units (procedures and functions). Packages are generally the most complicated and extended pieces of code PL/SQL developers will write. To make matters worse, the current array of PL/SQL development environments do not offer any tools for viewing and managing a package as a collection. A package is treated and presented no differently from a single function—just a whole bunch of lines of source code.
As a result, it is up to you to design and write your package to make it as readable and maintainable as possible. There are two fundamental strategies you can employ to help meet this objective:
Strategy 1: Use all available techniques to make your code as clean, modular, and structured as possible.
Strategy 2: Come up with a consistent coding style and format for your packages—and get people to follow that style.
As with the declaration sections of procedures and functions, you must (both in the package specification and body) declare all variables and data structures before you declare any program units. But what about the order of these program units themselves? As you can see from my banners, I always try to define all my private modules before any of my public modules. These are the building blocks used by the public programs. I group them together so they are easier to locate.
Is this ordering strictly necessary? Yes and no. Yes, you must define a private program before it is referenced by another program in the package (public or private). No, you do not have to group them together. You could instead define all private modules just before they are used by their public counterparts. This can make sense if the private program is only used by a single public program. If it is shared by many public programs (or other private ones, for that matter), then this placement does not accurately reflect its role in the package.
You can, by the way, place the definitions of the public program units anywhere in the package body (after the variable declarations)—even after they are referenced by another program. How is this possible? Since their headers have already been established in the package specification, the PL/SQL compiler has all the information it needs to resolve the reference.
As a rule, developers are much too careless about the names they give to their packages and the elements inside those packages, (most importantly, procedures and functions.) There are two aspects to coming up with the right names for your code elements:
The structure of the name should match the role that element plays in your code.
The name should reflect what the element does in your code.
Have you ever thought about the structure of the names you choose? PL/SQL is a computer language. It is much simpler than human languages like Japanese or English, but it still has many of the same grammatical components, such as nouns and verbs. RAISE_APPLICATION_ERROR, for example, contains a verb (RAISE)-noun (APPLICATION_ERROR) combination, as in: "Raise this error." The built-in function, SUBSTR, is an example of a noun (SUBSTR), as in: "if the substring is NULL, then ask for a dollar amount."
PL/SQL on the other hand, is more complicated than human languages because you, the developer, get to make up words in the language as you go. You define new nouns and verbs every time you declare a variable or define a new program. This means that each and every one of us is, at least in part, responsible for the integrity of the PL/SQL language. Keep this in mind as you name your program elements. Let's apply this consideration to packages.
First of all, the name of the package should always be structured as a noun. The package itself does not do anything, so it cannot and should not be an action verb. The package name declares, as simply as possible, the contents of the package.
Keep in mind that when you reference a package element outside of the package you must use dot notation (package.element). As a result, you will want to avoid redundancy in your package and element names.
Sure, we want our code to be flexible, in a practical sort of way. It is quite another thing to internalize this issue in the context of packages and figure out how to take full advantage of the package structure to offer maximum flexibility.
If a program is going to be widely reusable, it should be able to adapt to different circumstances to meet different needs. It is easy to talk about flexibility. I have found that when it comes to packages there are two basic ways to be flexible when writing programs for others to use:
Offer lots of parameters in the parameter lists of the package's functions and procedures. This is the traditional, well-worn path.
Provide toggles or on-off switches, distinct from the main programs of the package, which modify the behavior of those programs. This approach takes advantage of the package structure to offer a new way of doing things.
A toggle is a set of three programs: two procedures that allow you to turn a feature on or off, and a function to tell you the current status (on or off). The liberal application of toggles can transform the usability of your packages.
One of the most powerful aspects of the package is the ability to overload program units. When you overload, you define more than one program with the same name. These programs will differ in other ways (usually the number and types of parameters) so that at runtime the PL/SQL engine can figure out which of the programs to execute. You can take advantage of the overloading feature of packages to make your package-based features as accessible as possible.
Does overloading sound unfamiliar or strange? Well, have you ever used the TO_CHAR function? If so, then you have already been enjoying the creature comforts of overloading. TO_CHAR converts both numbers and dates to strings. Have you ever wondered why you don't have to call functions with names like TO_CHAR_FROM_DATE or TO_CHAR_FROM_NUMBER? Probably not. You probably just took TO_CHAR for granted, and that is how it should be.
In reality, there are two different TO_CHAR functions (both defined in the STANDARD package): one to convert dates and another to convert numbers. The reason that you don't have to care about such details and can simply execute TO_CHAR is that the PL/SQL runtime engine examines the kind of data you pass to TO_CHAR and then automatically figures out which of the two functions (with the same name) to execute. It's like magic, only it's better than magic: it's intelligent software!
To build packages that are both immediately useful and enhanceable over the long-run, you must avoid any kind of code duplication inside the package. You need to be ready, willing, and able to create private programs in your package to contain all the shared code behind the public programs of the package. The alternative is a debilitating reliance on the Windows cut-and-paste feature. Cut-and-paste will let you build rapidly—but what you will be building is a wide, deep hole from which you will never see the light of day.
You implement PL/SQL-based global data with package data. Package data is any data structure declared in a package body or specification. There are two kinds of package data: public data (declared in the specification) and private data (declared in the body).
What's the difference between public and private? Public global data is the proverbial "loose cannon" of programming. Public package data is certainly very convenient. Simply declare a few variables in a package specification and they are available from/to any module. If you need to get a piece of information, just grab it from the global. If you want to change the value of that variable, go at it. Reliance on global data structures, however, leads to two significant problems:
Loss of control. When you declare a data structure in the package specification, you lose control over that data structure's value. Since any program can write to it, you can never trust its value. Instead, you must trust developers to do the right thing when working with that variable. Now, I am as trusting as the next programmer, but anarchy really has little place in the world of software development.
Loss of flexibility. When you allow programmers to make direct references to global data, you lose the flexibility you need to enhance your application to take advantage of new features. Very specifically, you limit your ability to change the data structures used to implement your global data.
you never define variables in the specification of a package (except when explicitly needed that way, as discussed at the end of this section). Instead, you always declare the variable in the package body. You then provide a procedure to set the value of that variable and a function to retrieve the value of that variable.
There is one other case in which specification-based variables are useful: anchored declarations. You can anchor or base the declaration of a variable on another, predefined structure. To do this, you use the %TYPE and %ROWTYPE attributes. The most common way %TYPE is used is to anchor a local PL/SQL variable to a database column.
PL/Vision is a collection of PL/SQL packages and supporting SQL*Plus scripts that can change radically the way you develop applications with the PL/SQL language.
What can PL/Vision do for you? The possibilities are almost endless:
Improve your productivity. PL/Vision goes a long way towards helping you avoid reinventing the wheel. Need to change a long string into a word-wrapped paragraph? Use the PLVprs.wra procedure. Want to display the contents of a PL/SQL table? Call the PLVtab.display procedure. Need to log activity to the database or maybe even write your information to a PL/SQL table? Call the PLVlog.put_line program. By using PL/Vision, you write much less code yourself, and instead spend your time deciding which prebuilt components of PL/Vision to plug into your own applications. You are able to focus much more of your effort on implementing the business rules for your systems.
Decrease the number of bugs in your code and fix the bugs you do find more rapidly. Since you will be writing less code, you will minimize the opportunities for bugs to creep into your own programs. When you do have compile errors, you can call the PLVvu.err program to show you precisely where the error occurred in your program. Furthermore, PL/Vision packages offer many ways to trace the actions taken within those packages. When you need more information, you simply call the appropriate package toggle to turn on a trace and then run your test. When you are done testing and debugging, you turn off the trace mechanisms and put your application into production.
Help you develop and enforce coding standards and best practices. You will do this in two ways: first, by using packages that explicitly support coding standards, such as PLVgen; second, by examining the code behind PL/Vision. This PL/SQL library has provided numerous opportunities for me to put my own best practices for packages into action. You will, no doubt, find occasional violations of my best practices, but by and large the code in PL/Vision should provide a wealth of ideas and examples for your own development.
Increase the percentage of reusable code in your applications. The more you leverage PL/Vision, the fewer new programs you have to write yourself. And this advantage doesn't just accrue to individual developers. You can use PL/Vision across multiple applications—it can be part of a truly enterprise-wide object and module library.
Demonstrate how to modularize and build layers. I don't want you to simply use PL/Vision. I want you to learn how and why I built PL/Vision so that you can accomplish the same kind of development yourself. We all need to be fanatically devoted to modularizing code for maximum reusability. We all need to become sensitive to identifying program functionality that should be broken out into different layers. To some extent, you can develop such sensitivity only by practicing the craft of software construction. But you can also examine closely the work of others and learn from their example (both the good and the bad).
Inspire you to be creative, to take risks in your coding. I have found that the real joy of programming is to be found in trying out new ways of doing things. When you stretch boundaries—whether they are the boundaries of your own experience or those of the documented features of a language—you make discoveries. And when those discoveries turn out to be productive, you create new things.