perl5-dbi/DBD-mysql

Autocommit/ROLLBACK failures throw away the root cause error

Opened this issue · 0 comments

The C code implementation has a few blocks like this:

        if (
#if MYSQL_VERSION_ID >=SERVER_PREPARE_VERSION
            mysql_autocommit(imp_dbh->pmysql, bool_value)
#else
            mysql_real_query(imp_dbh->pmysql,
                             bool_value ? "SET AUTOCOMMIT=1" : "SET AUTOCOMMIT=0",
                             16)
#endif
           )
        {
          do_error(dbh, TX_ERR_AUTOCOMMIT,
                   bool_value ?
                   "Turning on AutoCommit failed" :
                   "Turning off AutoCommit failed"
                   ,NULL);
          return TRUE;  /* TRUE means we handled it - important to avoid spurious errors */
        }

In these cases, the driver is attempting to send SQL via mysql_real_query, where it could fail, but sends a generic "This thing failed" error message. The real root cause could be a connection failure, or a restart, or some other weird MySQL error message, but that's all thrown out here. Other do_error calls look like this:

        do_error(dbh, mysql_errno(imp_dbh->pmysql),
                mysql_error(imp_dbh->pmysql) ,mysql_sqlstate(imp_dbh->pmysql));

This queries the state of the last error and sends that bit. This is the real error message that matters.

Another case of this design bug is here:

    if (imp_dbh->has_transactions)
    {
      if (!DBIc_has(imp_dbh, DBIcf_AutoCommit))
#if MYSQL_VERSION_ID < SERVER_PREPARE_VERSION
        if ( mysql_real_query(imp_dbh->pmysql, "ROLLBACK", 8))
#else
        if (mysql_rollback(imp_dbh->pmysql))
#endif
            do_error(dbh, TX_ERR_ROLLBACK,"ROLLBACK failed" ,NULL);
    }

Again, it throws out the root cause error and sends out a generic error message. This code block is in dbd_db_destroy, but dbd_db_rollback actually sends out the right error message.