![]() |
|||||||||||||||||||||||||||||||||||||||||||||
|
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
|
||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||