Postgres Pro provides packages, which are similar to packages in Oracle, and can be useful when porting from PL/SQL to PL/pgSQL. This section explains differences between Postgres Pro's and Oracle's packages.
In Oracle, a package is a schema object that groups logically related types, global variables, and subprograms (procedures and functions). A package consists of a package specification and a package body (in general, the package body is optional). In the package specification, those items are declared that can be referenced from outside the package and used in applications: types, variables, constants, exceptions, cursors, and subprograms. The package body contains package subprogram implementation, private variable declarations, and the initialization section. The variables, types and subprograms declared in the package body cannot be used from outside the package.
The example below shows the package specification of the
PL/SQL counter package in
Oracle, which contains the global variable
n and the function inc:
CREATE PACKAGE counter IS
n int;
FUNCTION inc RETURN int;
END;
The function inc and the global variable
k (available only in the package body) are
declared in the counter package body in
Oracle.
CREATE PACKAGE BODY counter IS
k int := 3; -- the variable is available only in the package body
FUNCTION inc RETURN int IS
BEGIN
n := n + 1;
RETURN n;
END;
-- Package initialization
BEGIN
n := 1;
FOR i IN 1..10 LOOP
n := n + n;
END LOOP;
END;
Global package variables exist during the session lifetime. Note
that the package body contains the initialization section —
the code block executed once a session when any package element is
accessed for the first time. In Oracle,
package elements could be accessed using dot notation as follows:
package_name.package_element.
SET SERVEROUTPUT ON
BEGIN
dbms_output.put_line(counter.n);
dbms_output.put_line(counter.inc());
END;
/
1024
1025
A package in Postgres Pro is essentially
a schema that contains the initialization function and may contain only
functions, procedures and composite types.
The initialization function is a PL/pgSQL function
named __init__ that has no arguments and returns
void. The initialization function must be defined
before any other package function. All the variables declared in
the initialization function are global and public so they can be used
by functions of other packages using dot notation. For example, the
bar variable declared in the __init__
function of the foo package can be referenced
as foo.bar.
Package variables can be accessed only from
PL/pgSQL code. To retrieve the value
of the global variable in an SQL query (SELECT) or in
a DML operation (INSERT, UPDATE,
DELETE), add a getter function that returns the value
to the caller. For package variables declared as constant,
default definition syntax in PL/pgSQL is used.
The package initialization function is invoked automatically when any
of the following elements is accessed in the current session:
#package
modifier
The initialization function can be invoked manually to reset values
of package variables. To do that, you can also use a built-in function
plpgsql_reset_packages()
but it resets all the packages in the current session (similar to
DBMS_SESSION.RESET_PACKAGES in
Oracle).
The above example of the Oracle's
counter package after porting to
Postgres Pro will look like this:
CREATE PACKAGE counter
CREATE FUNCTION __init__() RETURNS void AS $$ -- package initialization
DECLARE
n int := 1;
k int := 3; -- the variable is global now
BEGIN
FOR i IN 1..10 LOOP
n := n + n;
END LOOP;
END;
$$
CREATE FUNCTION inc() RETURNS int AS $$
BEGIN
n := n + 1;
RETURN n;
END;
$$
;
You can use this package in Postgres Pro as follows:
DO $$
#import counter
BEGIN
RAISE NOTICE '%', counter.n;
RAISE NOTICE '%', counter.inc();
END;
$$;
NOTICE: 1024
NOTICE: 1025
There are some points worth noting.
Schemas cannot contain packages, as the package itself is a schema.
The package name must be distinct from the name of any existing package or schema in the current database.
A package may contain only composite types, global variables, procedures, and functions (for example, tables, views, sequences cannot be created in packages).
All package functions and variables are public. Package variables are available from any code block importing the package. Package specification and package body are not defined separately.
Package functions and code blocks importing them must be written in PL/pgSQL.
All package elements must be accessed from outside the package using qualified names, i.e. using package name. From inside the package, functions can access them directly.
Global package variables can be initialized when declared:
DECLARE x int := 42;
or later in the initialization function __init__.
For the external code using the package, it doesn't matter.
Types must be declared in the beginning of the package before the first subprogram.
The __init__ function must be the first
subprogram defined in the package.
To support packages in Postgres Pro, the following enhancements were introduced:
CREATE [OR REPLACE] PACKAGE and DROP
PACKAGE SQL
commands
Function modifiers start with # and are placed
between the function name and the DECLARE statement
(see example below).
#package Modifier
The modifier #package indicates that the function
should be treated as a package function, which means the following:
When the function is called, the package is initialized automatically if it hasn't been initialized in the current session.
The function can access the variables of its package directly.
The __init__ function must not contain the
#package modifier. A function with the
#package modifier can be created only in a schema
that already contains the __init__ function
(that is, in a package).
If you create a function within the CREATE OR REPLACE
PACKAGE, the #package modifier may be
omitted as it is added automatically in this command.
#import Modifier
The modifier #import indicates that the function
is supposed to work with variables of an outside package (or
a number of packages). This is called package import and means
the following:
When this function is accessed, the imported package is initialized automatically if it hasn't been initialized in the current session.
The function can access the variables of the imported package using package name. To import the package, use the following syntax:
#import packages_list
Here packages_list is the list of
imported package names. You can also import packages separately,
for example:
#import foo, bar
would mean the same as:
#import foo #import bar
All the packages imported in the __init__
function are automatically imported for other functions of the
package.
In the below example of using the #import modifier,
the showValues procedure calls
p of the htp package
and set_action of the dbms_application_info
package.
CREATE OR REPLACE PROCEDURE showValues(p_Str varchar) AS $$
#import htp, dbms_application_info
BEGIN
CALL dbms_application_info.set_action('Show hello');
CALL htp.p('<p>' || p_Str || '</p>');
END;
$$LANGUAGE plpgsql;
PL/pgSQL provides a built-in function
plpgsql_reset_packages() that deinstantiates
all the packages in this session to bring the session to the original
state.
SELECT plpgsql_reset_packages();
You can work with packages using regular schema-oriented commands (like
CREATE SCHEMA
and DROP SCHEMA)
with function modifiers but CREATE OR REPLACE
PACKAGE and DROP PACKAGE commands are
more convenient.
CREATE OR REPLACE PACKAGE
CREATE OR REPLACE
PACKAGE will either create a new package, or
replace the existing definition.
CREATE [OR REPLACE] PACKAGEpackage_namepackage_body;
Here package_name is the name of
the schema, and package_body is the
set of package creation commands with several caveats to be aware of:
The CREATE SCHEMA is executed automatically.
The #package modifier can be omitted —
all the functions are modified automatically.
No language is specified when creating functions.
Function and type names are specified without the package name.
The individual top-level subcommands in the package body do not end with semicolons.
The package name must be distinct from the name of any existing package or schema in the current database.
The example below shows how the package can be created using
the CREATE SCHEMA command.
CREATE SCHEMA foo; CREATE TYPE foo.footype AS (a int, b int); CREATE FUNCTION foo.__init__() RETURNS void AS $$ DECLARE x int := 1; BEGIN RAISE NOTICE 'foo initialized'; END; $$ LANGUAGE plpgsql; CREATE FUNCTION foo.get() RETURNS int AS $$ #package BEGIN RETURN x; END; $$ LANGUAGE plpgsql; CREATE FUNCTION foo.inc() returns void AS $$ #package BEGIN x := x + 1; END; $$ LANGUAGE plpgsql;
The following is an equivalent way of achieving the same result
using the CREATE PACKAGE command:
CREATE PACKAGE foo
CREATE TYPE footype AS (a int, b int)
CREATE FUNCTION __init__() RETURNS void AS $$
DECLARE
x int := 1;
BEGIN
RAISE NOTICE 'foo initialized';
END;
$$
CREATE FUNCTION get() RETURNS int AS $$
BEGIN
RETURN x;
END;
$$
CREATE FUNCTION inc() RETURNS void AS $$
BEGIN
x := x + 1;
ENG;
$$
;
You can replace the existing package with the following special considerations:
You can use the CREATE OR REPLACE PACKAGE to
replace only schemas that are packages (i.e. containing only
functions and composite types and the initialization function).
When any elements of the old package are not defined in the new
package, they are dropped if there are no dependent objects,
otherwise the CREATE OR REPLACE PACKAGE is
forced to fail.
If you replace the package without changing the function
signature, its body is replaced, while dependent objects still
remain. If the function signature changes, you would actually
be creating a new, distinct function. The latter would only
work when there are no dependent objects, otherwise the
CREATE OR REPLACE PACKAGE is forced to fail.
The same restriction applies to replacing types.
If the command fails due to existing dependent objects, the containing package is referenced by the error rather than the objects themselves.
CREATE OR REPLACE PACKAGE is a fully transactional
command so any mistakes when defining package elements would cause
it to fail entirely, and all the changes are discarded.
DROP PACKAGE
DROP PACKAGE
removes packages (schemas) from the database along with dependent
objects.
DROP PACKAGE [IF EXISTS] package_name_list [CASCADE];
Here package_name_list is the list of
packages to be dropped. The command is fully transactional so either
all of the packages are dropped, or none if the command fails.
This command is similar to DROP
SCHEMA with several caveats to be aware of:
You can use DROP PACKAGE to drop only
schemas that are packages (i.e. containing only functions and
composite types and the initialization function).
If the command fails due to existing dependent objects, the containing package is referenced by the error rather than the objects themselves.
Using the CASCADE option might make the command
find objects to be removed in other packages besides the one(s)
named. In this case, other packages are dropped as well.
All the dependencies mentioned in this section are standard Postgres Pro dependencies (see Section 5.14). For example, if a function uses other function signature in its own definition, the dependency is created but a single function call does not create one. When a function accesses package variables from outside the package, no dependency is created either (even a getter function).
In Oracle, a package is an atomic structure
that can be modified only as a whole with CREATE OR
REPLACE. As far as Postgres Pro Enterprise
is concerned, a package is a schema so its elements can be modified
separately with CREATE OR REPLACE PROCEDURE,
CREATE OR REPLACE FUNCTION, or ALTER
TYPE. Thus, you can easily replace a function or a type
that does not have dependencies rather than replace the package itself.
If you replace the package, you also need to replace all the
dependent packages and functions manually.
Since a package in Postgres Pro is a schema, the user should have the rights on the schema:
GRANT USAGE ON SCHEMA foo, bar TO hr_user;
If the package must be run with definer's rights, the user should have all the rights on the schema:
GRANT ALL ON SCHEMA hr_main TO hr_user;
If the package must be run with invoker's rights, the user should have specific rights on tables and views to be used in the package, for example:
GRANT SELECT demo.employee_tab TO hr_user;
Unlike Oracle, which stores all the information about package dependencies and in general about all PL/SQL objects (triggers, functions, procedures, etc.), Postgres Pro doesn't store dependencies at the level of function body and variables so these dependencies cannot be tracked when the package changes.
In Oracle, when the dependencies are
broken, the package becomes INVALID. In this case,
the package still exists in the database but it is not working.
Unlike that, in Postgres Pro any object
created or modified in the database must be valid.
So if any operation in Postgres Pro
leads to package invalidation (for example, the table used in
package type declaration is dropped), it cannot be executed —
first, the package must be deleted as it cannot remain invalid
in the database.
When modifying packages, make sure that no other sessions use them (which can be done by stopping the application before replacing the packages). Once done, restart the application, and updated package versions will be available in all the sessions. This limitation is caused by the lack of library cache in Postgres Pro (unlike Oracle) since each backend caches PL/pgSQL code. If the package is modified in one session, and another session still works with the old package version, it would cause inconsistency, especially if the list of global package variables changes. Currently there are no global locks for package modification (like latch in Oracle) and no package modification tracking (like “ORA-04068: existing state of packages has been discarded” in Oracle).
All package elements are available from outside the package. Unlike Oracle, packages are not separated into specification and body.
Clauses modifying procedure declaration (like
RESULT_CACHE, DETERMINISTIC
in Oracle) are not supported.
PL/SQL compiler directives are not
supported either (like INLINE pragma,
UDF pragma, etc.).