REF CURSOR gets OUT ?

Après avoir détricoté des erreurs ORA-00604 et ORA-01001 qui apparaissaient en manipulant des REF CURSOR comme paramètres depuis des clients ODP.NET et Java, je suis tombé sur une section de la documentation assez étonnante… Celle-ci suggère que vous ne devez pas utiliser de paramètre OUT de type REF CURSOR ; mais plutôt IN OUT. Voici ce que vous pouvez lire dans le manuel de référence PL/SQL d’Oracle 11.2, ce qui suit :

When declaring a cursor variable as the formal parameter of a subprogram:

  • If the subprogram opens or assigns a value to the cursor variable, then the parameter mode must be IN OUT.
  • If the subprogram only fetches from, or closes, the cursor variable, then the parameter mode can be either IN or IN OUT.

Il n’y a donc pas de cas pour lesquels utiliser les REF CURSOR comme paramètres OUT, et… Il se trouve que lorsque vous le faites, comme dans le programme ci-dessous, que vous manipulez le curseur en IN OUT dans la clause USING d’un EXECUTE IMMEDIATE et que passer ce curseur en OUT dans la procédure, cela fonctionne bien en PL/SQL mais pas à partir d’un programme y accédant via les OCI :

create or replace package arkz AS

type TYPE_CURSOR is ref cursor;
PROCEDURE test(pC OUT TYPE_CURSOR);
END arkz;
/

create or replace package body arkz as
procedure test(pC OUT type_cursor) is
begin
execute immediate
'begin open :rcCurseur for ''select 1 from dual''; end;'
USING IN OUT pC;
END test;
END arkz;
/

Voilà comment se manifeste le problème :

java Test604

Exception in thread "main" java.sql.SQLException: ORA-00604: error occurred at recursive SQL level 1
ORA-01001: invalid cursor
at oracle.jdbc.driver.T2CConnection.checkError(T2CConnection.java:759)
at oracle.jdbc.driver.T2CConnection.checkError(T2CConnection.java:661)
at oracle.jdbc.driver.T2CCallableStatement.executeForDescribe(T2CCallableStatement.java:544)
at oracle.jdbc.driver.T2CCallableStatement.executeForRows(T2CCallableStatement.java:727)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1315)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3576)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3677)
at oracle.jdbc.driver.OracleCallableStatement.execute(OracleCallableStatement.java:4714)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1374)
at TestWHITE604.main(TestWHITE604.java:19)

Le contournement simple ; le correctif n’est pas disponible sur ma plateforme mais ça fonctionnait bien en 10.2. Si vous êtes curieux, celui-ci est décrit dans la note « When Trying to Return a Ref Cursor Results in Ora-01001 Or Ora-00604 After Upgrade to 11.1.0.7 / 11.2 [ID 1106914.1]« .