Objectives:
___________________________________________________________________________________________________________________________________
PL/pgSQL is block structured:
[ Label ] [ DECLARE declarations ] BEGIN statements; END;Example:
CREATE FUNCTION somefunc() RETURNS INTEGER AS ' DECLARE quantity INTEGER := 30; BEGIN RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 30 quantity := 50; -- -- Create a sub-block -- DECLARE quantity INTEGER := 80; BEGIN RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 80 END; RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 50 END; ' LANGUAGE 'plpgsql';
PL/pgSQL has all SQL types. It is possible to create tuple variables using
%ROWTYPE
attribute.
Example showing this and also control structures:
DROP FUNCTION Test(INTEGER); CREATE FUNCTION Test(INTEGER) RETURNS INTEGER AS ' DECLARE StudRec Student%ROWTYPE; TransRec Transcript%ROWTYPE; Count INTEGER := 0; Id ALIAS FOR $1; BEGIN -- SELECT result must be a single tuple. SELECT INTO StudRec * FROM Student S WHERE S.Id = Id; -- Aggregate functions appear not to work. Manually iterate over -- entire result set to compute count. FOR TransRec IN SELECT * FROM Transcript T WHERE StudRec.Id = T.StuId LOOP Count := Count + 1; END LOOP; IF Count < 10 THEN RAISE NOTICE ''% has taken too few courses'', StudRec.Name; END IF; RETURN count; END; ' LANGUAGE 'plpgsql'; SELECT Test(666666666);
Trigger procedures must be a function with no parameters and a return type of
OPAQUE
.
Special variables automatically created in the top-level block:
NEW
--- new tuple value on UPDATE/INSERT row level
triggers. OLD
--- old tuple value on UPDATE/DELETE row level
triggers. The procedure must either return NULL
(or execute RAISE
EXCEPTION
) or a tuple matching the structure of the relation the trigger
was called on.
BEFORE
triggers return NULL
to signal that the
operation for this tuple should be skipped, RAISE EXCEPTION
to
abort the transaction, return a modified result, or do nothing (return
NEW
unchanged) to allow the intended result. AFTER
triggers can return NULL
with no effect.
Example:
CREATE FUNCTION RaiseCheck () RETURNS trigger AS ' BEGIN IF NEW.Salary > 1.05 * OLD.Salary THEN -- Excessive salary raise - limit it. RAISE NOTICE ''% given an excessive raise'', OLD.EmpName; NEW.Salary := 1.05 * OLD.Salary; END IF; -- Salary increase OK, proceed with transaction. RETURN NEW; END; ' LANGUAGE 'plpgsql'; CREATE TRIGGER LimitRaises BEFORE UPDATE ON Employee FOR EACH ROW EXECUTE PROCEDURE RaiseCheck();
___________________________________________________________________________________________________________________________________
Assignment:
~jillz/cs317/lab4_1
and ~jillz/cs317/lab4_2
to
one of your directories.
lab4_1
in an editor. Notice that it consists of three parts: a
clean-up section; a section which creates the payroll table, PL/pgSQL
function, and trigger; and a section which tests the trigger function. VerifySalary()
. Carefully
read the comments above this function so that you're clear on what needs to
be done. psql
to open your
personal database and execute the SQL code in your file. The output from the
run should be very similar to:
jillz=> \i lab4_1 DROP TRIGGER DROP FUNCTION DROP TABLE CREATE TABLE CREATE FUNCTION CREATE TRIGGER psql:lab4_1:64: ERROR: Tom has a negative salary name | salary ------+-------- (0 rows) INSERT 0 1 INSERT 0 1 psql:lab4_1:74: NOTICE: Tom reached the salary cap UPDATE 1 name | salary ------+-------- Jill | 895 Tom | 105Some
ERROR
messages may be present only on the first run.
Subsequently, they will be replaced with DROP
messages. CheckEmployeeFunc()
.
If the salary in question is greater than the manager's salary, cap the
salary to the manager's salary and raise a notice. The cap is ignored if the
employee has no manager, but a notice should be raised. CheckManagerTrigger
, and stored
procedure, CheckManagerFunc()
, to enforce the constraint that
no manager earn more than the sum of his or her employees' salaries. When
adjusting a salary, raise a notice. This trigger should enable on insert or
update events, should be a row-level trigger, and the condition should be
checked prior to the occurrence of the event.