Re: Problem with procedures returning a SYS_REFCURSOR which is not open/executed - possible fixes

"Martin J. Evans" <[email protected]>
Newsgroups gmane.comp.lang.perl.modules.dbi.sybase.devel
Organization Easysoft Limited
Message-ID <[email protected]>
On 15/01/13 15:04, Charles Jardine wrote:
> On 15/01/13 11:20, Martin J. Evans wrote:
>> On 15/01/13 10:56, Charles Jardine wrote:
>>> On 11/01/13 16:04, Martin J. Evans wrote:
>>>> I am using DBD::Oracle and calling a procedure which returns a reference
>>>> cursor. However, sometimes the reference cursor is not opened and only
>>>> the procedure knows this. The problem is if I call the procedure from
>>>> DBD::Oracle and the cursor is not opened I get an Oracle error saying
>>>> the cursor is not executed:
>>>>
>>>> test procedure:
>>>>     procedure p_n2(pcur OUT SYS_REFCURSOR) AS
>>>>     begin
>>>>        pcur := NULL;
>>>>     end;
>>>>
>>>> example perl:
>>>> my $s = $h->prepare(q/begin mypkg.p_n2(?); end;/);
>>>> $s->bind_param_inout(1, \my $cursor, 100, {ora_type => ORA_RSET});
>>>> $s->execute; # errors
>>>>
>>>> The error occurs because DBD::Oracle attempts to call dbd_describe on
>>>> the returned cursor (before perl land even sees it) and that code does
>>>> things like call OCIAttrGet for PARAM_COUNT etc which Oracle disallows
>>>> if the statement is not executed.
>
> [snip]
>
>>>
>>> Martin, I agree that you have found a bug which ought to be fixed.
>>> However I think that the bug is much more fundamental, and much
>>> simpler than your analysis suggests.
>>>
>>> The DBI convention for database null values is to represent them
>>> by Perl undefined values. There is no reason why this convention
>>> should not apply to result sets.
>>>
>>> The perl code needs to test the indicator variable associated with
>>> the supposed result set. If this variable indicates a null value,
>>> all processing of actual value returned by Oracle should be skipped,
>>> as with any other null value. The pre-created perl statement handle
>>> should be left alone. It may me used in a subsequent execute of the
>>> same statement with different bind values. The value returned to
>>> perl should be undef, not a reference to the magic statement handle.
>>>
>>
>> Thanks for your comments Charles.
>>
>> In effect I believe my second patch does what you describe. If you
>> define an output SYS_REFCURSOR in a procedure but don't open it you
>> still get a cursor back but it is only initialised and not executed. My
>> second patch (the one I prefer) looks at the state of the cursor and if
>> it is initialised but not executed it avoids creating a DBI sth and that
>> output parameter is seen as undef in perl land.
>>
>> If I've misunderstood you please put me right.
>
> When a variable of a REF CURSOR type is declared is is initially
> atomically null. It does not refer to cursor until it is OPENed,
> or set to a non-null value in some other way.
>
> Are you saying that, in the case of a NULL variable, the indicator
> variable does not indicate nullity?

I am saying when you have:

procedure fred(pcur OUT SYS_REFCURSOR) as
begin
pcur := NULL;
end;

then the output parameter DBD::Oracle sees appears to be a valid Oracle statement as you can call OCIAttrGet for OCI_ATTR_STMT_STATE and it works. The following snippet illustrates this:

    OCIAttrGet_stmhp_stat2(imp_sth, (OCIStmt*)phs->desc_h, &stmt_state, 0,
                                OCI_ATTR_STMT_STATE, status);

The desc_h in the parameters must be an OCIStmt * or surely this would not work.

Martin


-- 
Martin J. Evans
Easysoft Limited
http://www.easysoft.com
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.