PLpgSQL (i)

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [0/15]
❖ PLpgSQL

PLpgSQL = Procedural Language extensions to PostgreSQL

A PostgreSQL-specific language integrating features of:

Provides a means for extending DBMS functionality, e.g.

Details: PostgreSQL Documentation, Chapter 42
COMP3311 20T3 ♢ PLpgSQL (i) ♢ [1/15]
❖ Defining PLpgSQL Functions

PLpgSQL functions are created (and inserted into db) via:

CREATE OR REPLACE
   funcName(param1, param2, ....)
   RETURNS rettype
AS $$
DECLARE
   variable declarations
BEGIN
   code for function
END;
$$ LANGUAGE plpgsql;


Note: the entire function body is a single SQL string   ($$...$$)

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [2/15]
❖ PLpgSQL Examples

Example: function to compute x / y  "safely"

create or replace function
   div(x integer, y integer) returns integer
as $$
declare
   result integer;    -- variable
begin
   if (y <> 0) then   -- conditional
      result := x/y;  -- assignment
   else
      result := 0;    -- assignment
   end if;
   return result;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [3/15]
❖ PLpgSQL Examples (cont)

Example: function to compute n!

create or replace function
   factorial(n integer) returns integer
as $$
declare
   i integer;
   fac integer := 1;
begin
   for i in 1..n loop
      fac := fac * i;
   end loop;
   return fac;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [4/15]
❖ PLpgSQL Examples (cont)

Example: function to compute n! recursively

create function
   factorial(n integer) returns integer
as $$
begin
   if n < 2 then
      return 1;
   else
      return n * factorial(n-1);
   end if;
end;
$$ language plpgsql;

Usage:   select factorial(5);

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [5/15]
❖ PLpgSQL Examples (cont)

Example: handle withdrawl from account and return status message

create function
    withdraw(acctNum text, amount integer) returns text
as $$
declare bal integer;
begin
    select balance into bal
    from   Accounts
    where  acctNo = acctNum;
    if bal < amount then
        return 'Insufficient Funds';
    else
        update Accounts
        set    balance = balance - amount
        where  acctNo = acctNum;
        select balance into bal
        from   Accounts
        where  acctNo = acctNum;
        return 'New Balance: ' || bal;
    end if;
end;
$$ language plpgsql;

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [6/15]
❖ PLpgSQL Gotchas

Some things to beware of:

Summary: debugging PLpgSQL can sometimes be tricky.

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [7/15]
❖ Data Types

PLpgSQL constants and variables can be defined using:

There is also a CURSOR type for interacting with SQL.
COMP3311 20T3 ♢ PLpgSQL (i) ♢ [8/15]
❖ Data Types (cont)

Variables can also be defined in terms of:

Examples:

quantity    INTEGER;
start_qty   quantity%TYPE;

employee    Employees%ROWTYPE;
-- or
employee    Employees;

name        Employees.name%TYPE;

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [9/15]
❖ Syntax/Control Structures

Typical set of control structures, with extensions:

Assignment var := expr
SELECT expr INTO var
Selection

IF Cond1 THEN S1
ELSIF Cond2 THEN S2 ...
ELSE S END IF

Iteration

LOOP S END LOOP
WHILE Cond LOOP S END LOOP
FOR rec_var IN Query LOOP ...
FOR int_var IN lo..hi LOOP ...

Si  = list of PLpgSQL statements, each terminated by semi-colon

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [10/15]
❖ SELECT...INTO

Can capture query results via:

SELECT Exp1,Exp2,...,Expn
INTO   Var1,Var2,...,Varn
FROM   TableList
WHERE  Condition ...

The semantics:

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [11/15]
❖ SELECT...INTO (cont)

Assigning a simple value via SELECT...INTO:

-- cost is local var, price is attr
select price into cost
from   StockList
where  item = 'Cricket Bat';
cost := cost * (1+tax_rate);
total := total + cost;

The current PostgreSQL parser also allows this syntax:

select into cost price
from   StockList
where  item = 'Cricket Bat';

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [12/15]
❖ SELECT...INTO (cont)

Assigning whole rows via SELECT...INTO:

declare
   emp    Employees%ROWTYPE;
   -- alternatively,  emp  RECORD;
   eName  text;
   pay    real;
begin
   select * into emp
   from Employees where id = 966543;
   eName := emp.name;
   ...
   select name,salary into eName,pay
   from Employees where id = 966543;
end;

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [13/15]
❖ SELECT...INTO (cont)

In the case of a PLpgSQL statement like

select a into b from R where ...

If the selection returns no tuples

If the selection returns multiple tuples
COMP3311 20T3 ♢ PLpgSQL (i) ♢ [14/15]
❖ SELECT...INTO (cont)

An alternative to check for "no data found"

Use the special variable FOUND ...

Example of use:

select a into b from R where ...
if (not found) then
    -- handle case where no matching tuples b

COMP3311 20T3 ♢ PLpgSQL (i) ♢ [15/15]


Produced: 6 Oct 2020