1.9. Procedures, Functions, and PackagesPL/SQL offers the following structures to modularize your code in different ways:
In this section, we'll describe procedures, functions, and packages . Triggers are described in a later section. Object types are not covered in this chapter; few developers and even fewer database administrators use Oracle's version of object-oriented language features. 1.9.1. ProceduresA procedure is a module that performs one or more actions. Because a procedure call is a standalone executable statement in PL/SQL, a PL/SQL block could consist of nothing more than a single call to a procedure. Procedures are key building blocks of modular code, allowing you to both consolidate and reuse your program logic. 1.9.1.1. Structure of a procedureThe general format of a PL/SQL procedure is as follows: PROCEDURE [schema.]name [( parameter [, parameter ...] ) ] [AUTHID DEFINER | CURRENT_USER] IS [declaration statements] BEGIN executable statements [ EXCEPTION exception handler statements] END [name]; where each element is used in the following ways:
1.9.1.2. Calling a procedureA procedure is called as an executable PL/SQL statement. In other words, a call to a procedure must end with a semicolon (;) and be executed before and after other SQL or PL/SQL statements (if they exist) in the execution section of a PL/SQL block. The following executable statement runs the apply_discount procedure: BEGIN apply_discount( new_company_id, 0.15 ); -- 15% discount END; If the procedure does not have any parameters, then you call the procedure without any parentheses: display_store_summary; In Oracle8i Database and later, you can also include empty opening and closing parentheses, as in: display_store_summary( ); 1.9.2. FunctionsA function is a module that returns a value. Unlike a procedure call, which is a standalone executable statement, a call to a function can exist only as part of an executable statement, such as an element in an expression or the value assigned as the default in a declaration of a variable. Because a function returns a value, it is said to have a datatype. A function can be used in place of an expression in a PL/SQL statement having the same datatype as the function. Functions are particularly important constructs for building modular code. For example, every business rule or formula in your application should be placed inside a function. Every single-row query should also be defined within a function, so that it can be easily and reliably reused.
An application short on function definition and usage is likely to be difficult to maintain and enhance over time. 1.9.2.1. Structure of a functionThe structure of a function is the same as that of a procedure, except that the function also has a RETURN clause. The general format of a function is as follows: FUNCTION [schema.]name [( parameter [, parameter ...] ) ] RETURN return_datatype [AUTHID DEFINER | CURRENT_USER] [DETERMINISTIC] [PARALLEL ENABLE ...] [PIPELINED] IS [declaration statements] BEGIN executable statements [EXCEPTION exception handler statements] END [ name ]; where each element is used in the following ways:
1.9.2.2. Calling a functionA function is called as part of an executable PL/SQL statement wherever an expression can be used. The following examples illustrate how various functions can be invoked.
1.9.3. ParametersProcedures and functions can both use parameters to pass information back and forth between the module and the calling PL/SQL block. The parameters of a module are at least as important as the code that implements the module (the module's body). Sure, you have to make certain that your module fulfills its promise. But the whole point of creating a module is that it can be called, ideally by more than one other module. If the parameter list is confusing or badly designed, it will be very difficult for other programmers to make use of the module, and the result is that few will bother. And it doesn't matter how well you have implemented a program if no one uses it. PL/SQL offers many different features to help you design parameters effectively. This section covers all elements of parameter definition. 1.9.3.1. Defining parametersFormal parameters are defined in the parameter list of the program. A parameter definition parallels closely the syntax for declaring variables in the declaration section of a PL/SQL block. There are two important distinctions: first, a parameter has a passing mode while a variable declaration does not; and second, a parameter declaration must be unconstrained. A constrained declaration is one that constrains or limits the kind of value that can be assigned to a variable declared with that datatype. An unconstrained declaration is one that does not limit values in this way. The following declaration of the variable company_name constrains the variable to 60 characters: DECLARE company_name VARCHAR2(60); When you declare a parameter, however, you must leave out the constraining part of the declaration: PROCEDURE display_company (company_name IN VARCHAR2) IS ... 1.9.3.2. Actual and formal parametersWe need to distinguish between two different kinds of parameters: actual and formal parameters. The formal parameters are the names that are declared in the parameter list of the header of a module. The actual parameters are the values or expressions placed in the parameter list of the actual call to the module. Let's examine the differences between actual and formal parameters using the example of tot_sales. Here, again, is the tot_sales header: FUNCTION tot_sales (company_id_in IN company.company_id%TYPE, status_in IN order.status_code%TYPE := NULL) RETURN std_types.dollar_amount; The formal parameters of tot_sales are:
These formal parameters do not exist outside of the function. You can think of them as placeholders for real or actual parameter values that are passed into the function when it is used in a program. How does PL/SQL know which actual parameter goes with which formal parameter when a program is executed? PL/SQL offers two ways to make the association:
1.9.3.3. Parameter modesWhen you define the parameter, you also specify the way in which it can be used. There are three different modes of parameters:
The mode determines how the program can use and manipulate the value assigned to the formal parameter. You specify the mode of the parameter immediately after the parameter name and before the parameter's datatype and optional default value. The following procedure header uses all three parameter modes: PROCEDURE predict_activity (last_date_in IN DATE, task_desc_inout IN OUT VARCHAR2, next_date_out OUT DATE) The predict_activity procedure takes in two pieces of information: the date of the last activity and a description of the activity. It then returns or sends out two pieces of information: a possibly modified task description and the date of the next activity. Because the task_desc_inout parameter is IN OUT, the program can both read the value of the argument and change the value of that argument. Positional notation. In every example so far, I have employed positional notation to guide PL/SQL through the parameters. With positional notation, PL/SQL relies on the relative positions of the parameters to make the correspondence: it associates the Nth actual parameter in the call to a program with the Nth formal parameter in the program's header. With the following tot_sales example, PL/SQL associates the first actual parameter, :order.company_id, with the first formal parameter, company_id_in. It then associates the second actual parameter, N, with the second formal parameter, status_in: new_sales := tot_sales (:order.company_id, 'N'); FUNCTION tot_sales (company_id_in IN company.company_id%TYPE, status_in IN order.status_code%TYPE := NULL) RETURN std_types.dollar_amount; Now you know the name for the way compilers pass values through parameters to modules. Positional notation is certainly the most obvious and common method. Named notation. With named notation , you explicitly associate the formal parameter (the name of the parameter) with the actual parameter (the value of the parameter) right in the call to the program, using the combination symbol =>. The general syntax for named notation is: formal_parameter_name => argument_value Because you provide the name of the formal parameter explicitly, PL/SQL no longer needs to rely on the order of the parameters to make the association from actual to formal. So, if you use named notation, you do not need to list the parameters in your call to the program in the same order as the formal parameters in the header. You can call tot_sales for new orders in either of these two ways: new_sales := tot_sales (company_id_in => order_pkg.company_id, status_in =>'N'); new_sales := tot_sales (status_in =>'N', company_id_in => order_pkg.company_id); You can also mix named and positional notation in the same program call: :order.new_sales := tot_sales (order_pkg.company_id, status_in =>'N'); If you do mix notation, however, you must list all of your positional parameters before any named notation parameters, as shown in the preceding example. 1.9.4. PackagesA package is a grouping or packaging together of PL/SQL code elements. Packages provide a structure (both logically and physically) in which you can organize your programs and other PL/SQL elements such as cursors, TYPEs, and variables. They also offer significant, unique functionality, including the ability to hide logic and data from view, and to define and manipulate global, or session-persistent, data. 1.9.4.1. Rules for building packagesThe package is a deceptively simple construct. In a small amount of time, you can learn all the basic elements of package syntax and rules, but you can spend weeks (or more) uncovering all the nuances and implications of the package structure. In this section, we review the rules you need to know to build packages. To construct a package, you must build a specification and, in almost every case, a package body. You must decide which elements go into the specification and which are hidden away in the body. You also can include a block of code that Oracle will use to initialize the package. The package specification. The specification of a package lists all the elements in that package that are available for use in applications, and it provides all the information a developer needs to use elements defined in the package (often referred to as an API, or application programming interface). A developer should never have to look at the implementation code in a package body to figure out how to use an element in the specification. Here are some rules to keep in mind for package specification construction:
Here is a very simple package specification illustrating these rules: 1 CREATE OR REPLACE PACKAGE favorites_pkg 2 AUTHID CURRENT_USER 3 IS 4 -- Two constants; notice that I give understandable 5 -- names to otherwise obscure values. 6 7 c_chocolate CONSTANT PLS_INTEGER := 16; 8 c_strawberry CONSTANT PLS_INTEGER := 29; 9 10 -- A nested table TYPE declaration 11 TYPE codes_nt IS TABLE OF INTEGER; 12 13 -- A nested table declared from the generic type. 14 my_favorites codes_nt; 15 16 -- A REF CURSOR returning favorites information. 17 TYPE fav_info_rct IS REF CURSOR RETURN favorites%ROWTYPE; 18 19 -- A procedure that accepts a list of favorites 20 -- (using a type defined above) and displays the 21 -- favorite information from that list. 22 PROCEDURE show_favorites (list_in IN codes_nt); 23 24 -- A function that returns all the information in 25 -- the favorites table about the most popular item. 26 FUNCTION most_popular RETURN fav_info_rct; 27 28 END favorites_pkg; -- End label for package As you can see, a package specification is, in structure, essentially the same as a declaration section of a PL/SQL block. One difference, however, is that a package specification may not contain any implementation code. The package body. The package body contains all the code required to implement the package specification. A package body is not always needed, but it is required when any of the following conditions is true:
Structurally, a package body is very similar to a procedure definition. Here are some rules particular to package bodies:
Here is an implementation of the favorites_pkg body: CREATE OR REPLACE PACKAGE BODY favorites_pkg IS -- A private variable g_most_popular PLS_INTEGER := c_strawberry; -- Implementation of procedure PROCEDURE show_favorites (list_in IN codes_nt) IS BEGIN FOR indx IN list_in.FIRST .. list_in.LAST LOOP DBMS_OUTPUT.put_line (list_in (indx)); END LOOP; END show_favorites; -- Implement the function FUNCTION most_popular RETURN fav_info_rct IS retval fav_info_rct; null_cv fav_info_rct; BEGIN OPEN retval FOR SELECT * FROM favorites WHERE code = g_most_popular; RETURN retval; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN null_cv; END most_popular; END favorites_pkg; -- End label for package 1.9.4.2. Rules for calling packaged elementsIt doesn't really make any sense to talk about running or executing a package (after all, it is just a container for code elements). However, you will certainly want to run or reference those elements defined in a package. A package owns its objects, just as a table owns its columns. To reference an element defined in the package specification outside of the package itself, you must use the same dot notation to fully specify the name of that element. Let's look at some examples. The following package specification declares a constant, an exception, a cursor, and several modules: CREATE OR REPLACE PACKAGE pets_inc IS max_pets_in_facility CONSTANT INTEGER := 120; pet_is_sick EXCEPTION; CURSOR pet_cur (pet_id_in IN pet.id%TYPE) RETURN pet%ROWTYPE; FUNCTION next_pet_shots (pet_id_in IN pet.id%TYPE) RETURN DATE; PROCEDURE set_schedule (pet_id_in IN pet.id%TYPE); END pets_inc; To reference any of these objects, I preface the object name with the package name, as follows: DECLARE -- Base this constant on the id column of the pet table. c_pet CONSTANT pet.id%TYPE:= 1099; v_next_apppointment DATE; BEGIN IF pets_inc.max_pets_in_facility > 100 THEN OPEN pets_inc.pet_cur (c_pet); ELSE v_next_appointment:= pets_inc.next_pet_shots (c_pet); END IF; EXCEPTION WHEN pets_inc.pet_is_sick THEN pets_inc.set_schedule (c_pet); END; To summarize, there are two rules to follow in order to reference and use elements in a package:
1.9.4.3. Package dataPackage data consists of variables and constants that are defined at the package level that is, not within a particular function or procedure in the package. The scope of the package data is therefore not a single program, but rather the package as a whole. In the PL/SQL runtime architecture, package data structures persist (hold their values) for the duration of a session (rather than the duration of execution for a particular program). If package data is declared inside the package body, then that data persists for the session but can be accessed only by elements defined in the package itself (private data). If package data is declared inside the package specification, then that data persists for the session and is directly accessible (to both read and modify the value) by any program that has EXECUTE authority on that package (public data). If a packaged procedure opens a cursor, that cursor remains open and is available throughout the session. It is not necessary to define the cursor in each program. One module can open a cursor while another performs the fetch. In addition, package variables can carry data across the boundaries of transactions because they are tied to the session rather than to a single transaction. |