PL/SQL specificities

PL/SQL specificities #

Autonomous transactions #

We often find PRAGMA associated with autonomous transactions in PL/SQL. This last notion does not exist in PostgreSQL.

It is possible to emulate autonomous transactions through a dblink, but it is a particularly counter-efficient solution that consumes resources. Indeed, the use of a dblink will cause a new database connection. The cost of a new connection is significant and a additional connection will require to properly size the max_connections parameter and will consume additional memory and CPU time.

The following Oracle PL/SQL code shows the declaration of a procedure with AUTONOMOUS_TRANSACTION pragma:

CREATE PROCEDURE LOG_ACTION (username VARCHAR2, msg VARCHAR2)
IS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO table_tracking VALUES (username, msg);
  COMMIT;
END log_action;

A possible rewrite with a dblink involves filling in the information to connect to the local PostgreSQL instance:

CREATE EXTENSION dblink;  
 
CREATE OR REPLACE FUNCTION log_action(username TEXT, msg TEXT)
  RETURNS void AS $$
BEGIN  
    perform dblink_connect('pragma', format(
      'dbname=%s user=test password=test', current_database()
    ));  
    perform dblink_exec('pragma', format(
      'insert into table_tracking values (%s, %s);', username, msg
    ));
    perform dblink_exec('pragma','commit;');  
    perform dblink_disconnect('pragma');
END;  
$$ LANGUAGE plpgsql;

Another alternative, which appeared in version 9.5 with background workers, is to rely on the extension pg_background to deport the execution of a procedure to a new transaction using of a wrapper procedure and the pg_background_launch method of the extension.

Another rewrite of the previous code would look like this with pg_background, with the creation of a calling procedure:

-- Create the function we will be "remotely" calling:
CREATE OR REPLACE FUNCTION log_action_atx(username TEXT, msg TEXT)
RETURNS void AS $$
BEGIN
  INSERT INTO table_tracking VALUES (username, msg);
END;
$$ LANGUAGE plpgsql;

-- Now we can write the calling function
CREATE OR REPLACE FUNCTION log_action(username TEXT, msg TEXT)
RETURNS void AS $$
DECLARE
  v_query text;
BEGIN
  v_query := format(
    'SELECT true FROM log_action_atx (%s, %s)', 
    quote_nullable(username), quote_nullable(msg)
  );

  PERFORM pg_background_result(
    pg_background_launch(v_query)
  );
END;
$body$
LANGUAGE plpgsql SECURITY DEFINER;

The log_action method behaves exactly as if it were executing a statement in a autonomous transaction.

References:

VARRAY collections #

VARRAY collections in Oracle packages are migrated to PostgreSQL arrays. They usually need a rewrite of the code using them.

When a VARRAY is a simple array of a scalar datatype, there is less rewriting work to be done than when dealing with a %ROWTYPE VARRAY. In this case, there is quite a lot of work.

The following code:

DECLARE
   TYPE Calendar IS VARRAY(366) OF DATE;

will be converted to:

CREATE TYPE calendar AS (date[366]);

The next one, won’t work without modification:

TYPE t_tab_emp IS VARRAY (1000) OF emp%ROWTYPE;
...
tab_emp t_tab_emp;

Associative arrays and nested tables #

TABLE OF collections are usually used to declare functions returning a set of records. As a consequence, the TABLE OF types can be replaced by a RETURNS TABLE or a RETURNS SETOF data_type for simple data types. Please refer to PIPELINED attribute and PIPE ROW instruction for an example where a TABLE OF collection is not necessary.

Thus, the following declaration:

CREATE TYPE information IS TABLE OF VARCHAR2(255);

will be converted to an array of varchar(255):

CREATE TYPE information AS VARCHAR(255)[];

The NUMBER INDEX clause has no equivalent, though. For instance, the following declaration:

TYPE t_list_qlf_id IS TABLE OF NUMBER INDEX BY VARCHAR2(5);

cannot be directly converted. One can use the hstore contrib module, or the JSONB datatype, to emulate this feature.

References:


Previous
Cursors
Next
Oracle's packages