| This page is user contributed documentation. See the bottom of the page for information about the author. | |
When creating a pocket agenda application, it's quite useful to have a mechanism
inhibiting intervals overlapping (i.e. first job starting at 10:00, lasting two
hours and second job starting at 11:00, lasting two hours). This article
is a possible implementation method for PostgreSQL 6.5 and maybe for PostgreSQL
7.1. ;)
After two sweaty weeks worried about a trigger which did not work, I can finally
and proudly explain the problem I had and how I solved it.
I am writing a web based application (Apache, PHP, PostgreSQL) for managing a music hall reservation system and here I faced a problem I'd never thought about. How to avoid two different reservations overlapping? In this case a "unique key" constraint is not enough to guarantee reservations will be correctly inserted because the key "oneness" itself cannot guarantee that two reservations are not partially overlapped (you can only see this with a very aimed SELECT).
Possible solutions :
Since the problem was generally recognized, and since the extra checking structure would have been boring but necessary for safely guaranteeing the right insertion and change, I opted for the latter (b).
PostgreSQL allows you to launch a procedure before or after you do an INSERT, UPDATE or DELETE transaction. This launched procedure is called a trigger. If you specify to launch it before though, it could fail and force the whole operation to fail.
PostgreSQL allows you to write the trigger associated procedure in C, SQL, PL/pgSQL, or PL/TCL. I used PL/pgSQL because I thought it was the quickest way to implement the trigger, but it could also be written in the other languages (being better performing in C (Code is better ;-)).
To have a trigger equipped table in PostgreSQL you must (in order) :
Here you have the minimum working example; more documentation can be found at www.postgresql.org in the PL/pgSQL chapter ( for pgsql 6.5 Programmer's Guide, III volume, cap. 42 ).
CREATE TABLE tritab (
idr int4,
nome text,
start int4,
len int4);
Here you created the table. idr is a unique
record identifier (you cannot have two records with the same identifier). start
is the starting reservation time. len
is the reservation time.
CREATE FUNCTION trifun () RETURNS OPAQUE AS '
DECLARE
myrec RECORD;
BEGIN
/* se insert verifica se ci sono record vecchi che si
intersecano */
/* if insert verifies if there are old intersecting records
*/
IF TG_OP = ''INSERT'' THEN
SELECT * INTO myrec FROM tritab
WHERE
start
< (NEW.start + NEW.len) AND
(start
+ len) > NEW.start;
IF FOUND THEN
RAISE
EXCEPTION ''INSERT failed:
intersection
with record % at (%,%)'',
myrec.idr,
myrec.start, myrec.len;
END IF;
END IF;
/* se update come insert con in piu il constraint che
l id non sia quello del record modificato */
/* if update like insert plus check on id, assuring that
it's not the one modified */
IF TG_OP = ''UPDATE'' THEN
SELECT * INTO myrec FROM tritab
WHERE
start
< (NEW.start + NEW.len) AND
(start
+ len) > NEW.start AND
idr <>
OLD.idr;
IF FOUND THEN
RAISE
EXCEPTION ''UPDATE failed:
intersection
with record % at (%,%)'',
myrec.idr,
myrec.start, myrec.len;
END IF;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
Here you created the function to be triggered. I'll promptly give you a private explanation after you have studied that chapter in the PostgreSQL manual. Some tips :
CREATE TRIGGER tritri BEFORE INSERT OR UPDATE ON tritab
FOR EACH ROW EXECUTE PROCEDURE trifun();
This linked the function as a trigger to the table.
Now, when launching sequentially these two example insertions :
INSERT INTO tritab ( idr, nome, start, len ) values ( 1, 'one', 10,
2);
INSERT INTO tritab ( idr, nome, start, len ) values ( 2, 'two', 11, 2);
the second will fail.
Goal.
Thanks to
DaDoS e NdK di ircnet/#programmazione
Copyright © 2001 Matteo
Nastasi (matteo.nastasi@milug.org),
Translated by Martino
Bana (martino.bana@milug.org)