DBI / S::S / DBD::CSV ... related hackathon

2013-01-15 Thread Jens Rehsack

Hi all,

as I know some of us are located close together, I suggested
a hackathon especially for Pure-Perl drivers, their tests and
integration.

My goals were:

1) having a lot of DBD::CSV tests (DBI related like ChopBlanks)
   moved or copied and maintained in DBI

   == a lot of tests simply test former DBD::File features,
   from times where DBD::File and DBD::CSV were much closer
   together and not so many other Pure-Perl DBD's in wildlife

   But now - there're DBD::DBM, DBD::AnyData, DBD::Sys, ...
   and upcoming DBD::Mem - which all should benefit from the
   impressive test cases written in DBD::CSV (and not only
   a turn later with next DBI release ^^)

2) let DBD::CSV use DBI::DBD to create tests for DBI::Nano, Gofer,
   different baackends/serializers ...

   == DBI::DBD has a neat feature function (dbd_edit_mm_attribs)
   to create tests using some different whatsoever:

   Prefix - means
   * p = DBI_PUREPERL=2
   * g = DBI_AUTOPROXY='dbi:Gofer:transport=null;policy=pedantic'
   * n = DBI_SQL_NANO=1

   All with restrictions for filename patterns etc.

   This function needs to be refactored to provide that
   functionality for other modules, too - and then used for
   DBD::DBM (create test combos for several backends and
   serializers), DBD::CSV (create test combos for S::S vs.
   Nano and Text::CSV vs. Text::CSV_XS), maybe SQL::Statement
   to create test combos for each available S::S using DBD
   (like currently done by looping e.g. t/02execute.t in
   SQL-Statement)

We (Merijn and myself) discussed that roughly in IRC - but decided
it would be a good idea to increase the count of affected/invited
people (like end-users of our services as ribasushi or Dijkmat ^^).

I suggested to find a location like the Unperfekthaus
(http://www.unperfekthaus.de/) in reachable distance to all
attendees.

So at first I would be great if all interested persons can
tell if they would like to join - let's say until end of January
(for a date in March/April 2013) with a limit how far they're
willing to travel.

Everyone is invited (regardless of his/her personal participation)
to suggest topics.

Second step is to create a more or less complete plan for those
who want which goals shall be reached (to approve whether there're
prerequisites which needs to be fulfilled before ...).

Thanks for attention,
Jens


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

2013-01-15 Thread Charles Jardine

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.

An easy solution is to just open an empty cursor if the procedure cannot
open a real one by doing something like:

open pcur for select 1 from dual;

but I don't like that as DBD::Oracle will make dozens of calls and do
quite a bit of work in dbd_describe which is wasting time and the
purpose of the change to my procedure is to speed this application up
not slow it down.


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.

--
Charles Jardine - Computing Service, University of Cambridge
c...@cam.ac.ukTel: +44 1223 334506, Fax: +44 1223 334679


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

2013-01-15 Thread Martin J. Evans

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.

An easy solution is to just open an empty cursor if the procedure cannot
open a real one by doing something like:

open pcur for select 1 from dual;

but I don't like that as DBD::Oracle will make dozens of calls and do
quite a bit of work in dbd_describe which is wasting time and the
purpose of the change to my procedure is to speed this application up
not slow it down.


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.

Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com


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

2013-01-15 Thread Charles Jardine

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?

--
Charles Jardine - Computing Service, University of Cambridge
c...@cam.ac.ukTel: +44 1223 334506, Fax: +44 1223 334679


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

2013-01-15 Thread Martin J. Evans

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


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

2013-01-15 Thread Charles Jardine

On 15/01/13 16:01, Martin J. Evans wrote:

On 15/01/13 15:04, Charles Jardine wrote:


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.


In this case the REF CURSOR variable in question is explicitly null.
I would expect the value returned via OCI to be accompanied by an
indicator variable with a value of -1,indicating a null value.
If this is the case, the value of the output variable is,
to quote the OCI manual, 'unchanged'. It should be ignored.

I am suggesting that the indicator variable should be tested before
the looking at the value. If indicator is -1, the value could be
the cursor returned by a previous execution of the same statement
handle.

--
Charles Jardine - Computing Service, University of Cambridge
c...@cam.ac.ukTel: +44 1223 334506, Fax: +44 1223 334679


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

2013-01-15 Thread Martin J. Evans

On 15/01/2013 17:52, Charles Jardine wrote:

On 15/01/13 16:01, Martin J. Evans wrote:

On 15/01/13 15:04, Charles Jardine wrote:


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.


In this case the REF CURSOR variable in question is explicitly null.
I would expect the value returned via OCI to be accompanied by an
indicator variable with a value of -1,indicating a null value.
If this is the case, the value of the output variable is,
to quote the OCI manual, 'unchanged'. It should be ignored.

I am suggesting that the indicator variable should be tested before
the looking at the value. If indicator is -1, the value could be
the cursor returned by a previous execution of the same statement
handle.


Thanks Charles.

I think your explanation means DBD::Oracle is even more broken than I 
thought wrt to output cursors.


Looks like I need to do a bit more reading. Thanks for the pointer.

Out of interest I looked at the code a little more and see the 
descriptor in the D::O's phs is freed and reallocated pre-execute. The 
parameter is also rebound. In pp_exec_rset it does (note my comment 
labelled MJE):


if (pre_exec) {/* pre-execute - allocate a statement handle - 
MJE it does not do this now */

sword status;

/* extproc deallocates everything for us */
if (is_extproc)
return 1;

/* MJE what is the following test supposed to be - always true? */
if (!phs-desc_h || 1) { /* XXX phs-desc_t != OCI_HTYPE_STMT) */
if (phs-desc_h) {
OCIHandleFree_log_stat(imp_sth, phs-desc_h, 
phs-desc_t, status);

phs-desc_h = NULL;
}
phs-desc_t = OCI_HTYPE_STMT;
OCIHandleAlloc_ok(imp_sth, imp_sth-envhp, phs-desc_h, 
phs-desc_t, status);

 }

phs-progv = (char*)phs-desc_h;
phs-maxlen = 0;

OCIBindByName_log_stat(imp_sth, imp_sth-stmhp, phs-bndhp, 
imp_sth-errhp,

(text*)phs-name,
(sb4)strlen(phs-name),
phs-progv,
0,
(ub2)phs-ftype,
NULL, /* using phs-indp triggers ORA-01001 errors! */
NULL,
0,
0,
NULL,
OCI_DEFAULT,
status);

However, as you said the phs-indp is -1 as you said. I will correct my 
change and make it check indp first.


As you know there is a lot of OCI code in DBD::Oracle and I'm not 
familiar with it all by a long way so I'm always grateful for any 
pointers/help.


Martin


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

2013-01-15 Thread Martin J. Evans

On 15/01/2013 22:20, Martin J. Evans wrote:

On 15/01/2013 17:52, Charles Jardine wrote:

On 15/01/13 16:01, Martin J. Evans wrote:

On 15/01/13 15:04, Charles Jardine wrote:


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.


In this case the REF CURSOR variable in question is explicitly null.
I would expect the value returned via OCI to be accompanied by an
indicator variable with a value of -1,indicating a null value.
If this is the case, the value of the output variable is,
to quote the OCI manual, 'unchanged'. It should be ignored.

I am suggesting that the indicator variable should be tested before
the looking at the value. If indicator is -1, the value could be
the cursor returned by a previous execution of the same statement
handle.


Thanks Charles.

I think your explanation means DBD::Oracle is even more broken than I 
thought wrt to output cursors.


Looks like I need to do a bit more reading. Thanks for the pointer.

Out of interest I looked at the code a little more and see the 
descriptor in the D::O's phs is freed and reallocated pre-execute. The 
parameter is also rebound. In pp_exec_rset it does (note my comment 
labelled MJE):


if (pre_exec) {/* pre-execute - allocate a statement handle - 
MJE it does not do this now */

sword status;

/* extproc deallocates everything for us */
if (is_extproc)
return 1;

/* MJE what is the following test supposed to be - always 
true? */

if (!phs-desc_h || 1) { /* XXX phs-desc_t != OCI_HTYPE_STMT) */
if (phs-desc_h) {
OCIHandleFree_log_stat(imp_sth, phs-desc_h, 
phs-desc_t, status);

phs-desc_h = NULL;
}
phs-desc_t = OCI_HTYPE_STMT;
OCIHandleAlloc_ok(imp_sth, imp_sth-envhp, phs-desc_h, 
phs-desc_t, status);

 }

phs-progv = (char*)phs-desc_h;
phs-maxlen = 0;

OCIBindByName_log_stat(imp_sth, imp_sth-stmhp, phs-bndhp, 
imp_sth-errhp,

(text*)phs-name,
(sb4)strlen(phs-name),
phs-progv,
0,
(ub2)phs-ftype,
NULL, /* using phs-indp triggers ORA-01001 errors! */
NULL,
0,
0,
NULL,
OCI_DEFAULT,
status);

However, as you said the phs-indp is -1 as you said. I will correct 
my change and make it check indp first.


As you know there is a lot of OCI code in DBD::Oracle and I'm not 
familiar with it all by a long way so I'm always grateful for any 
pointers/help.


Martin
hmm, unfortunately, the indp always seems to be -1 (The selected value 
is null, and the value of the output variable is unchanged) even when an 
opened cursor is returned as indp is not passed to OCIBindByName because 
(see above code) it is commented out in the OCIBindByName because it 
apparently triggers ORA-01001 errors but we've no idea why - sigh. So 
I assume somewhere else set indp to -1, it certainly wasn't OCIBindByName.


I see loads of code setting indp so I created an indp2 in the phs and 
passed it to OCIBindByName above and it is always 0 (Oracle assigned an 
intact value to the host variable) whether a null cursor is returned or 
not. It also did not seem to trigger ORA-01001 (invalid cursor) errors. 
Also the test suite works with the indp set in the OCIBindByName. What a 
PITA. I really wish when people write code like this they comment why 
better.


So it would seem resurrecting the phs-indp in the OCIBindByName does 
not currently give me -1. Starting to wish I never started this. My 
current change is better (in that it at least works whereas the previous 
code did not at all) but you seem to suggest it is incomplete and that 
concerns me. However, I've not been able to see what you suggested 
should happen. I've already proved this speeds our application up a lot 
compared with having to put a daft select 1 from dual in to just make 
DBD::Oracle work so it would be a shame to fall at the last hurdle. Any 
other ideas Charles?


Bear in mind I cannot be getting a value from a previous execute as my 
test code only does one execute so perhaps when you bind a SYS_REFCURSOR 
you get a valid stmt handle back even if it is not executed. It seems 
this must be the case since I can call OCIAtrrGet(OCI_ATTR_STMT_STATE) 
on it after only one execute and it returns OCI_STMT_STATE_INITIALIZED.


As always, most grateful for