Search code examples
c++mysqlqtodbcqtsql

why is my transaction not rolling back? Qt Mysql odbc driver


I'm using Qt 4.8.3 and MySQL ODBC 3.51 Driver. When my transaction fails because of a duplicate unique Id in the second table the insert in the first table is not rolled back. Can anyone spot the error?

struct Property  //see OMG's Property Service
{
    std::string name;
    boost::any value;
    Property();
    Property(const std::string &inName, const boost::any &inValue):name(inName),value(inValue){}

};

Property myFunction(QSqlDatabase &db, int amount)
{
    assert(db.driver()->hasFeature(QSqlDriver::Transactions) == true);
    db.transaction();
    QSqlQuery query(db);
    Property ret("MyPropertyTag",0);
    try{
        query.exec("LOCK TABLES table1 WRITE, table2 WRITE");
        if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); }
        for(int i=0;i<amount;i++)
        {
            query.exec("INSERT INTO table1 (someUniqueValue, newestId, Created) VALUES ('"+QString::number(i)+"', '1', NOW())");
            if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); }
            query.exec("SELECT id FROM table1 WHERE someUniqueValue = '"+QString::number(i)+"'");
            if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); }
            if(query.next() == false)   { throw std::exception("no result for insert id"); }
            auto Id1 = query.value(0).toString();

            query.exec("INSERT INTO table2 (table1_id, Changed, Created) VALUES ('"+Id1+"', NOW(), NOW())");
            if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); }
            query.exec("SELECT Id, table1_id FROM table2 ORDER BY Id DESC LIMIT 1");
            if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); } //lets say this throws
            if(query.next() == false || query.value(1).toString() != Id1)   { throw std::exception("no result for inserted id"); }
            auto Id2 = query.value(0).toString();

            query.exec("UPDATE table1 SET newestId = '"+Id2+"' WHERE Id = '"+Id1+"'");
            if(query.lastError().isValid()) { throw std::exception(query.lastError().text().toAscii()); }
        }
        db.commit();
        ret.value = amount;
    } catch(std::exception &e) {
        query.finish();
        db.rollback();
        ret.value = std::string("error:") + e.what();
    }
    query.exec("UNLOCK TABLES");
    query.finish();
    return ret;
}

Solution

  • Your Tables problably are Myisam, changed them to Innodb to allow transactions.

    To change the AutoCommit (I'm not sure if it's necessary) you can try it this way:

    mysql> SET autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    

    but this will be working only with the current connection, if you need to apply to all the DB you need to change the DB variables.