-------------------------------------------------------------------------------
-- corr_pl_exceptions.sql

-------------------------------------------------------------------------------
-- exceptions et erreurs

-- on ajoute simplement des exceptions a achat
/* 
contraintes : 
client : check solde>=0
voyage : idc, ids : fk, and check 1<=jour<=365
codes pour check : -2290, fk : -02291
update client set solde = -3000;
insert into voyage values(-2, -2, 1);
*/

create or replace procedure achat(
  c client.idc%type, s sejour.ids%type, j voyage.jour%type)
is
  p sejour.prix%type;
  pb_check exception;
  pragma exception_init(pb_check, -2290);
  pb_fk exception;
  pragma exception_init(pb_fk, -2291);
  /* rem : la fk de sejour ne peut pas etre violee ici car l'identifiant
     est utilise pour selectionner avant, mais alors cela genere un 
     no_data_found */
begin
  select prix into p from sejour where ids = s;
  update client set solde = solde - p where idc = c;
  insert into voyage values (c, s, j);
exception
  when pb_check 
    then dbms_output.put_line('violation de check (client.solde ou voyage.jour)');
  when pb_fk then dbms_output.put_line('violation de fk : client n''existe pas');
  when no_data_found then dbms_output.put_line('sejour n''existe pas');
  when others then dbms_output.put_line('erreur pas prevue...');
end;
/

select * from client;
select * from sejour;
select * from voyage;

/*
tests :

Toutes possibilites de parametres indadequats pour achat.
But : les verifications sont faites par le systeme, pas par le programmeur :
beaucoup plus puissant, fiable.

achat avec parametres corrects
solde idc < prix ids
j > 365
ids inexistant
idc inexistant

(On pourrait tester aussi : table inexistante, si on a le temps, ce n'est pas 
le but.)

Il suffit d'avoir une ligne dans chaque table, et de tester les 4 cas ci-dessus.
*/

-------------------------------------------------------------------------------
-- contrainte non SQL

/*
On considere une variante de l'enonce : on ne declenche pas exactement le meme 
comportement, mais un comportement le plus proche possible (plus precisement, on
ne va pas empecher la mise a jour faisant la violation, alors qu'avec les 
contraintes SQL elle n'a pas lieu.
*/

/*
fonction contrainte :

select count(ids)
into pb
from sejour s
where ids in
  select ids
  from voyage
  group by ids, jour
    having count(idc) > s.nbpl

return pb >= 1
*/

/*
principe :
trigger : sur toutes tables concernees
corps trigger : si contrainte violee alors raise appli error
mises a jour dangereuses :
  voyage : insert, update of ids, jour
  sejour : update of ids, nbpl
before ou after ? il faut avoir fait la mise a  jour pour pouvoir verifier
for each row ? non : la contrainte est verifiee globalement (une seule requete 
  SQL)
*/

/*
trigger contrainte
after ... toutes les mises a  jour dangereuses ...
on voyage ... + le meme sur sejour (peut-on mettre : "voyage, sejour" ?)
begin
  if not contrainte
    then raise_appication_error(-20002, 
           'attention : une mise a jour a mis la base dans un etat dans lequel
            la contrainte blabla est violee'
end

tests : tout essayer
*/

/*
reflexions hors programme pour eviter la mise a jour :
- trigger before + savepoint dans le trigger ? non
- simuler les mises a jour ou leur inverse parait decidable, mais trop ad hoc,
  (utiliser if insert, if update, etc. ?)
- nouvelles options dans la semantique des triggers, et before/after (tester 
  avec count) ?
*/

-------------------------------------------------------------------------------
