On Tue, Jul 21, 2015 at 01:33:34PM +0100, Martin J. Evans wrote:
> Long, sorry.

No problem. The whole topic is a bit of a mess.

> On 20/07/15 18:00, Tim Bunce wrote:
> >On Mon, Jul 20, 2015 at 02:54:53PM +0100, Martin J. Evans wrote:
> >>On 20/07/15 14:15, Tim Bunce wrote:
> >>
> >>I think that would work for me - I'm happy to test it our here if you want 
> >>to give it a go.
> >>
> >>IIRC, when this was last discussed the problem is that some drivers
> >>might not set DBIc_ROW_COUNT so you can't just use DBIc_ROW_COUNT.
> >
> >Hence the check that DBIc_ROW_COUNT is not zero. Since the DBI code sets
> >it to zero before the call, if it's non-zero after the call we can be
> >sure that the driver has set it.
> >
> >>In fact, I just checked, and DBD::ODBC does not seem to call
> >>DBIc_ROW_COUNT other than to set it to 0 in ODBC.xsi (which is code
> >>from DBI anyway). Does that sound right?
> >
> >Nope. Is it setting the underlying structure member directly?
> 
> no. All it does is it has a RowCount member in its own imp_sth_st structure 
> which is a SQLLEN (64 bits on 64 bit machines and 32 on 32). Then it:
> 
> o dbd_db_execute returns the number of rows or -1 or -2 (error)
>   At the end of dbd_st_execute if the affected rows is bigger than INT_MAX 
> and warnings are
>   on, it warns the rowcount has been truncated and changes the row count to 
> INT_MAX.

That's reasonable. Hopefully we can do better though.

> o has odbc_st_rows (because it is defined in dbd_xsh.h and I believed
>   you needed to implement most of these in the DBD) which casts the
>   internal RowCount to an int as odbc_st_rows is defined as returning an int.

The DBI provides a default rows method, in C, that returns DBIc_ROW_COUNT.
So a driver that stores the row count in DBIc_ROW_COUNT doesn't need to
provide a rows method at all (if all it needs to do is return the count).

That translates into not defining the dbd_st_rows macro. If that's not
defined then the rows method in Driver.xst won't get compiled in so
there'll be no call to a driver-provided dbd_st_rows.

> DBD::ODBC also has its own odbc_rows which returns an IV to workaround this 
> issue in DBI when I found it back in 2012.

If DBD::ODBC switched to using DBIc_ROW_COUNT then you could remove
dbd_st_rows/odbc_rows.  (It seems unlikely that sizeof(IV) would ever me
less than sizeof(SQLLEN) but that might be worth an assertion anyway.)


> Looking at 'do' in DBI.pm it just does:
> 
>     sub do {
>       my($dbh, $statement, $attr, @params) = @_;
>       my $sth = $dbh->prepare($statement, $attr) or return undef;
>       $sth->execute(@params) or return undef;
>       my $rows = $sth->rows;
>       ($rows == 0) ? "0E0" : $rows;
>     }
> 
> so doesn't that just end up in dbd_st_rows?

Assuming the driver is using that default do() method, then it'll
end up in dbd_st_rows if the driver has defined a dbd_st_rows macro,
else it'll end up in the DBI's default rows() method.

> >>If a driver is supposed to set DBIc_ROW_COUNT I'd rather change the
> >>drivers I maintain to do that especially since in ODBC and 64bit
> >>SQLRowCount already returns a 64 bit value.
> >
> >Yeap. That's best.
> 
> See above, I don't see how that fits in right now.

Is the only outstanding issue now the 'int' return type on some various
dbd_st_* functions?

> I tried to check my assumptions and this is what I found:
> 
> o DBD::ODBC has its own 'do' method because it can use SQLExecDirect instead 
> of prepare/execute. This returns the rows affected correctly as it returns an 
> SV created from the SQLLEN RowCount. So, the do method in DBI (shown above) 
> is neither here nor there for DBD::ODBC.
> 
> o DBD::ODBC has a dbd_st_rows which seems to get called if someone calls the 
> rows method.
> dbd_st_rows is defined in dbd_xsh.h as returning an int so this is wrong.

And can simply be removed, per the above.

> o 'execute' or dbd_st_execute returns the rows and is again defined in 
> dbd_xsh as returning an int.
> 
> I don't see where DBIc_ROW_COUNT comes in unless you are saying every time a 
> DBD discovers the row count it should call DBIc_ROW_COUNT macro.

DBIc_ROW_COUNT is just a macro for an IV in the imp_sth structure. Most,
if not all, compiled drivers that aren't using DBIc_ROW_COUNT are simply
using their own integer element in the imp_sth structure. In the case of
DBD::Pg that's declared as a plain int type.

So I'd hope and expect a driver can simply use DBIc_ROW_COUNT _instead of_
whatever it's currently using.

> >I also noticed something I should have seen before: dbd_st_rows() is
> >defined as returning an int. I _think_ it would be safe to change the
> >definition to returning an IV since it's only used internally by drivers
> >via the Driver.xst template file that does:
> >
> >     XST_mIV(0, dbd_st_rows(sth, imp_sth));
> 
> Unless I'm missing something I think that will break most drivers as when I 
> grepped cpan I found most drivers implement dbd_st_rows as:
> 
> int dbd_st_rows {
>   code
> }

[Sigh] I'm getting a bit rusty at C. I'd forgotten that hurdle.
The int return type affects dbd_st_execute, dbd_st_rows, and dbd_db_do4.

The fix is to do what we've done before when the signature of
dbd_db_login and dbd_db_do changed. E.g., first we added a dbd_db_login6
macro and then a dbd_db_login6_sv macro. Drivers just define the one
they want to use.

So I propose adding #ifdef's for dbd_st_execute_iv, dbd_st_rows_iv, and
dbd_db_do4_iv. Driver can then define those if they want to use them
(and add a prereq on the appropriate version of DBI.)

Sound ok?

Tim.

Reply via email to