Tim Bunce wrote:
On Wed, Mar 26, 2008 at 03:01:54PM +0000, Martin Evans wrote:
Hi,

I was attempting to tidy up some code in DBD::ODBC and noticed quite a number of tests using DBIc_ACTIVE which attempt to signal an error when the database is not active (not connected). Having never seen these errors I wrote a quick test script and got no errors when calling prepare, do etc after disconnecting. Investigating, it appears there is a bug in DBD::ODBC which fails to report these errors correctly but this led me to a few questions:

dbd_st_prepare, dbd_db_execdirect, dbd_st_tables, dbd_st_primary_keys and a host of odbc private functions in dbdimp.c receive combinations of an sth, dbh or both and do something like:

if (!DBIc_ACTIVE(imp_dbh)) {
  error code
}

I would like to commonise some of this. I know I can get my private sth from an sth (D_imp_sth) and similarly for database handle (D_imp_dbh) and I can get my private dbh from an sth with D_imp_dbh_from_sth but the DBIh_SET_ERR_CHAR macros needs a handle - how do you get SV *dbh from imp_dbh? (DBIc_PARENT_COM perhaps?)

You can use newRV_noinc((SV*)DBIc_MY_H(imp_dbh)) but there are some caveats.
I'd recommend just passing both the h and the imp_xxh to all functions.

Thanks. The problem was that some methods have a dbh, some have an sth and some have both and the code to check for the connection and report the error was predominantly the same in case so shouted out to be a single function. I ended up doing:

int dbd_db_execdirect( SV *dbh,
                       char *statement )
{

   if ((dbh_active = check_connection_active(dbh)) == 0) return 0;
   .
   .
}
int
dbd_st_prepare(SV *sth, imp_sth_t *imp_sth, char *statement, SV *attribs)
{
   if ((dbh_active = check_connection_active(sth)) == 0) return 0;
   .
   .
}

etc for other methods.

static int check_connection_active(SV *h)
{
    D_imp_xxh(h);
    struct imp_dbh_st *imp_dbh = NULL;
    struct imp_sth_st *imp_sth = NULL;

    switch(DBIc_TYPE(imp_xxh)) {
      case DBIt_ST:
        imp_sth = (struct imp_sth_st *)imp_xxh;
        imp_dbh = (struct imp_dbh_st *)(DBIc_PARENT_COM(imp_sth));
        break;
      case DBIt_DB:
        imp_dbh = (struct imp_dbh_st *)imp_xxh;
        break;
      default:
        croak("panic: check_connection_active bad handle type");
    }

    if (!DBIc_ACTIVE(imp_dbh)) {
        DBIh_SET_ERR_CHAR(
            h, imp_xxh, Nullch, 1,
            "Cannot allocate statement when disconnected"
            " from the database", "08003", Nullch);
        return 0;
    }
    return 1;
}

which seems to work and I hope is ok.

Also, who does DBI allow prepare in a driver to be called when the dbh was disconnected?

s/who/why/? The presumption is that the underlying database API will
return an error in that situation (with a suitable native error message)
so the DBI needn't waste time being pedantic and second guessing what's
allowable. (Why just disallow prepare, for example?)

Tim.

I just picked prepare as an example but yes it applies to other methods as well. Just thought it seemed reasonable that DBI could do this and save each driver from doing it but that does not work if they want to report different states.

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

Reply via email to