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.