2016-04-20 9 views
0

die folgende Implementierung einer generischen Funktion Angenommen, eine Datenbank zur Abfrage MySQL:Ist dies eine sichere Implementierung einer generischen SQL-Ausführungsfunktion?

bool execute(const sql::SQLString query, std::vector<sql::ResultSet*> &results) 
{ 
    // ConnectionPool holds premade connections to the database 
    sql::Connection* conn = ConnectionPool::getInstance()::getConnection(); 
    std::unique_ptr<sql::Statement> stmt; 
    bool success = false; 

    try 
    { 
     stmt.reset(conn->createStatement()); 
     stmt->execute(query); 

     do 
     { 
      results.push_back(stmt->getResultSet()); 
     } while (stmt->getMoreResults()) 

     success = true; 
    } 
    catch (...) 
    { 
     // Other catch() statements are not a part of this question 
     std::cerr << "Exception caught!" << std::endl; 
     success = false; 
    } 

    conn->commit(); 
    ConnectionPool::getInstance()::returnConnection(conn); 

    return success; 
} 

Nach this example für die Ergebnisse einer Abfrage abzurufen, muss die ResultSet explizit gelöscht werden. In Bezug auf die obige Implementierung bedeutet dies, dass der Vektor von ResultSet-Zeigern sicher zu verwenden ist (d. H. Die Objekte, auf die sie zeigen, werden durch das Löschen der Erstellanweisung nicht gelöscht)?

Auch mache ich irgendetwas Unaussprechliches Böses mit dieser Implementierung?

+0

Sollten Sie eine Festschreibung vornehmen, wenn eine Ausnahme aufgetreten ist? Solltest du versuchen, deinen Verbindungs-Commit zu umgehen? In jedem Fall muss dies dokumentiert werden. Im Beispiel löschen sie das Ergebnis vor dem stmt; abhängig davon, wie verrückt ihre Ressourcenverwaltung ist ... Wenn die Ergebnismenge gelöscht werden soll, sollte auch ein Vektor eindeutiger Zeiger auf sie verwaltet werden, nicht rohe Zeiger. Die Lebensdauer von stmt ist länger als die Lebensdauer der Verbindung, ist das ein Problem? – Yakk

+0

@Yakk Unser Modell ist immer die Verbindung zu machen; entweder am Ende der Funktion oder in try() und catch(). Die Verbindung wird an den Pool zurückgegeben, wo sie von einem anderen Funktionsaufruf benötigt wird. Daher ist ihre Lebensdauer länger als die Anweisung. – Frostfyre

Antwort

0

Die folgende Implementierung ist sicherer. Dies liegt daran, dass das ResultSet an die Verbindung und nicht an die Anweisung gebunden ist. Wenn die Verbindung vor dem Lesen der Ergebnisse geschlossen, zerstört oder für einen anderen Zweck verwendet wird, können die ResultSet-Objekte inaktiv werden und eine Abbruchbedingung verursachen. Es ist sicherer, wenn die aufrufende Funktion die Verbindung selbst verwaltet.

Beachten Sie, dass eine bessere Implementierung für den sql::Connection*-Parameter std::unique_ptr< sql::Connection >& wäre, aber meine persönlichen Bedürfnisse diktieren dies kann derzeit nicht getan werden.

bool execute(sql::Connection *conn, const sql::SQLString query, std::vector< std::unique_ptr<sql::ResultSet> > &results) 
{ 
    std::unique_ptr<sql::Statement> stmt; 
    bool success = false; 

    try 
    { 
     stmt.reset(conn->createStatement()); 
     stmt->execute(query); 

     do 
     { 
      results.emplace_back(stmt->getResultSet()); 
     } while (stmt->getMoreResults()) 

     success = true; 
    } 
    catch (...) 
    { 
     // Other catch() statements are not a part of this question 
     std::cerr << "Exception caught!" << std::endl; 
     success = false; 
    } 

    conn->commit(); 
    return success; 
} 
Verwandte Themen