Control structures

Control structures #

Loops and control structures don’t require a lot of work, except for the GOTO and FORALL instructions.

FOR REVERSE loop #

The FOR ... REVERSE has also the peculiarity of needing to revers the min and max boundaries in FOR ... IN ... REVERSE min..max.

For an Oracle code:

FOR i IN REVERSE 1..10 BY 2 LOOP
  ...
END LOOP;

One should convert to:

FOR i IN REVERSE 10..1 BY 2 LOOP
  ...
END LOOP;

Another important difference is FOR loops on queries (other than cursors). In PL/SQL, the target variables are implicitly declared, while they have to be declared in PostgresQL. One will have to declare those variables in the DECLARE block of the function. This has the advantage of keeping the variable accessible out of the loop.

Thus, the following PL/SQL extract:

DECLARE
  v_date1 DATE, v_date2 DATE;
BEGIN
  FOR r IN (
    SELECT numberprgn FROM t2
     WHERE status = 'ordered' AND assigned_dept = 'departt'
  )
LOOP
  SELECT min(datestamp) INTO v_date1
    FROM T1 WHERE NUMBER = r.numberprgn  AND description = 'state1';

  SELECT min(datestamp) INTO v_date2
    FROM T1 WHERE NUMBER = r.numberprgn  AND description = 'state2';
END LOOP;

will be converted this way, declaring v_date1 and v_date2 as timestamp data type:

DECLARE
  v_date1 TIMESTAMP, v_date2 TIMESTAMP;
  r record;
BEGIN
  FOR r IN (
    SELECT numberprgn FROM t2
    WHERE status = 'ordered' AND assigned_dept = 'departt'
  )
LOOP
  SELECT min(datestamp) INTO STRICT v_date1
    FROM T1 WHERE number = r.numberprgn  AND description = 'state1';

  SELECT min(datestamp) INTO STRICT v_date2
    FROM T1 WHERE number = r.numberprgn  AND description = 'state2';
END LOOP;

References:

GOTO instruction #

The GOTO instruction has no equivalent and imposes a rewriting. Usage of this instruction is usually not advised, but it is not that seldom used to step out of a loop or get to the next iteration.

Given this PL/SQL code:

LOOP
  FETCH current_cursor INTO current;
  EXIT WHEN curs_courant%NOTFOUND;
  IF v_cat = 'YYY' THEN
    v_cat := current;
    GOTO BEGINNING;
  END IF;
  IF current = 'N1' THEN
    v_cat := current;
    GOTO ENDING;
  END IF;
  ...
<<BEGINNING>>
NULL;
END LOOP;
<<ENDING>>
CLOSE current_cursor;

should be rewritten this way:

LOOP
  FETCH current_cursor INTO current;
  IF NOT FOUND THEN
    EXIT;
  END IF;
  IF v_cat = 'YYY' THEN
    v_cat := current;
    CONTINUE;
  END IF;
  IF current = 'N1' THEN
    v_cat := current;
    EXIT;
  END IF;
  ...
END LOOP;
CLOSE current_cursor;

FORALL loops #

A FORALL loop is used to iterate over the lines of an Oracle collection. As there are no collections in PostgreSQL, there is no FORALL loop either. It’s implementation is simple enough with PostgreSQL, as it is about iterating over the array replacing the collection.

Thus, the following PL/SQL procedures:

CREATE OR REPLACE PROCEDURE allEmployees
IS
  TYPE v_array IS varray(50) OF CHAR(14);
  arr_emp v_array;
BEGIN
  SELECT ename BULK COLLECT INTO arr_emp
  FROM emp
  ORDER BY ename;
  FORALL i IN 1..arr_emp.COUNT
    dbms_output.put_line('|name'||arr_emp..(i)||'i'|| i);
END allEmployees;

becomes:

CREATE OR REPLACE FUNCTION allEmployees()
RETURNS VOID AS $body$
DECLARE
  arr_emp CHAR(14)[];
BEGIN
  arr_emp := array ( SELECT ename FROM emp ORDER BY ename);
  FOR i IN array_lower(arr_emp,1)..array_upper(arr_emp,1) LOOP
    RAISE NOTICE '|name % i %', arr_emp[i], i;
  END LOOP;
END;
$body$ LANGUAGE plpgsql;

Previous
Triggers conversion
Next
PL/SQL code conversion