PL/SQL by Example / Package
In previous parts, most examples were demonstrated at the level of a single anonymous PL/SQL block. In practice, however, it is more appropriate to unify functionality into logical units, called packages. The advantage is not only the grouping of related objects and thus clearer and more readable code, but also higher performance (the package is kept in memory for the duration of the session), simpler administration of access rights, and the ability to shield internal logic from other objects in the database.
A package consists of two parts. The mandatory part is the specification (sometimes also called the “header”) containing only the list and definition of objects, the optional part is the body, which further describes the objects and their function. The rule is that what is declared in the header is publicly visible, while what is declared only in the package body is private.
CREATE OR REPLACE PACKAGE MyPackage AS -- public declarative part g_Public NUMBER:=10; END; /
CREATE OR REPLACE PACKAGE BODY MyPackage AS -- private declarative part g_Private NUMBER:=20; G_CONSTANT CONSTANT NUMBER:=30; END; /
BEGIN dbms_output.put_line(MyPackage.g_Public); -- 10 MyPackage.g_Public:=30; dbms_output.put_line(MyPackage.g_Public); -- 30 -- CANNOT MyPackage.g_Private:=40; -- CANNOT dbms_output.put_line(MyPackage.g_Private); END;
It is possible to wrap not only variables and constants in a package, but also types, procedures, functions, cursors, and user-defined exceptions.
CREATE OR REPLACE PACKAGE MyPackage
AS
-- public type
TYPE Varchar_TT IS TABLE OF VARCHAR2(4000) INDEX BY PLS_INTEGER;
-- public function specification
FUNCTION mf_ArrayToString(
pt_Array MyPackage.Varchar_TT
, p_Delimiter CHAR:=', '
) RETURN VARCHAR2;
END;
/
CREATE OR REPLACE PACKAGE BODY MyPackage
AS
-- public function body
FUNCTION mf_ArrayToString(
pt_Array MyPackage.Varchar_TT
, p_Delimiter CHAR:=', '
) RETURN VARCHAR2
IS
l_Result VARCHAR2(4000);
BEGIN
FOR i IN 1..pt_Array.COUNT LOOP
l_Result:=l_Result||p_Delimiter||pt_Array(i);
END LOOP;
RETURN LTRIM(l_Result, p_Delimiter);
END;
END;
/
DECLARE lt_Array MyPackage.Varchar_TT; BEGIN lt_Array(1):='one'; lt_Array(2):='two'; dbms_output.put_line(MyPackage.mf_ArrayToString(lt_Array)); -- one, two END;
If we refer to an internal method inside the package, it is important that its definition physically precedes the definition of the method from which we are calling it. The difference in the specification and use of a public versus an internal method is illustrated in the following example.
CREATE OR REPLACE PACKAGE MyPackage
AS
-- public parameterless function specification
FUNCTION mf_ReturnPrivate
RETURN NUMBER;
-- public parameterless procedure specification
PROCEDURE mp_SetPrivateToHundred;
END;
/
CREATE OR REPLACE PACKAGE BODY MyPackage
AS
-- private variable
g_Private NUMBER;
-- private procedure
PROCEDURE mpi_SetPrivate(
p_Parameter NUMBER
)
IS
BEGIN
g_Private:=p_Parameter;
END;
-- public parameterless function body
FUNCTION mf_ReturnPrivate
RETURN NUMBER
IS
BEGIN
RETURN g_Private;
END;
-- public parameterless procedure body
PROCEDURE mp_SetPrivateToHundred
IS
BEGIN
-- call to private procedure
mpi_SetPrivate(100);
END;
END;
/
BEGIN -- CANNOT MyPackage.mpi_SetPrivate(100); MyPackage.mp_SetPrivateToHundred; dbms_output.put_line(MyPackage.mf_ReturnPrivate); -- 100 END;
Just like stored procedures from the previous chapter, functions in packages can also be used in SQL queries.
SELECT MyPackage.mf_ReturnPrivate FROM dual ; -- 100