!
!
!
!
!
!
!
!

PL/SQL, fermeture du curseur (03/10/2003)
Doit on fermer un curseur implicite ? Si, dans une cursor for loop, une exception est déclenchée, est-ce que le curseur est fermé de façon implicite ?
Et j'avais dit d'utiliser:
Begin
....
....
  begin
   FOR r_compte IN c_compte
   LOOP
     ...
     ... <--- exception
   END LOOP
   exception
     when others then
     if c_compte%isopen then close c_compte; end if;
    end;
...
end;
Pourquoi ?
Théoriquement Oracle DOIT fermer son curseur, mais comme je disais, ce n'est pas sur qu'il le fasse. Encore théoriquement Oracle doit fermer un curseur à la sortie du bloc PL/SQL ou il est déclaré (nettoyage de la mémoire, en gros).
Du point de vue logique, il le fait ... mais il ne libère généralement pas sa petite fenêtre PGA, en pensant 'Je vais la réutiliser plus tard'. Si on utilise une (seule) boucle, ça passe, MAIS j'ai rencontré le phénomène suivant (En Oracle 7 et 8): Soit une boucle de x millions d'occurrences. Pour chaque occurrence, on ouvre un curseur implicite for ... in loop; ... end loop;
En gros le même curseur est ouvert plusieurs millions de fois. Ici et la je rencontrais l'erreur 'too many cursors open', d'ou la déduction que Oracle ne fermait pas son curseur. OK, on lui force la main avec close forcé. neuf cas sur dix: 'Curseur invalide' (normal, théoriquement), d'ou la construction if ..%isopen then close.
Pire: même si on dit de le fermer, ce n'est pas sur qu'il le fasse (physiquement), d'ou les paramètres plus ou moins documentés:
_close_cached_open_cursors, etc
Il faut faire attention également au client qui attaque la base. J'ai remarqué que pour le même type de boulot, si l'on utilise JDBC par exemple, nous avons besoin de (beaucoup) plus de fenêtres PGA (open_cursors dans init.ora > 50 (défaut), valeur ridicule).
En Oracle 9i, le code suivant fonctionne sans devoir forcer la fermeture du curseur:
SET serveroutput ON
CREATE TABLE test1 (c1 number);
TRUNCATE TABLE test1; 
INSERT INTO test1 VALUES (0);
COMMIT;
DECLARE
res NUMBER;
CURSOR c IS SELECT c1 FROM test1;
BEGIN
DBMS_OUTPUT.enable(1000000);
FOR i IN 1 .. 10000 LOOP
  BEGIN
  FOR j IN c LOOP
     res := 1/j.c1;
  END LOOP;
  EXCEPTION
    WHEN OTHERS THEN
      DBMS_OUTPUT.put_line(sqlerrm);
  END;
dbms_output.put_line('Boucle ' || i);
END LOOP;
END;
/
RC

Copyright © 1998-2002 Radu Caulea, TAFORA MAJ 06/11/2006 !
Commentaires et suggestions radu[CHEZ]tafora.fr