2010-07-27 67 views
1

我正在用SQLite編寫一個C++測試應用程序,爲此我製作了一個小包裝器。SQLite在完成語句時發生奇怪的崩潰

我不斷地讀取一些數據,第四次,當它完成聲明時,它崩潰。我確定我將每個準備好的陳述與finalize匹配起來。第四次,在崩潰之前,數據已經成功讀取。

一個好奇的事情是:我現在正在顯示返回的語句指針(通過printf(「%p」)),並且在第一次讀取時它總是0048D228。崩潰的是66676767.

我重新運行該程序,地址再次相同。

這裏是我的課的重要組成部分:

class SQLiteResultSet 
{ 
private: 
    sqlite3_stmt *stmt; 
public: 
    inline SQLiteResultSet(sqlite3_stmt *pStmt) : stmt(pStmt) {} 
    inline bool next() { return sqlite3_step(stmt) == SQLITE_ROW; } 
    inline const unsigned char *textColumn(int colNum) { 
     return sqlite3_column_text(stmt, colNum); 
    } 
    inline const int intColumn(int colNum) { 
     return sqlite3_column_int(stmt, colNum); 
    } 
    inline void close() { 
     if(isOpen()) { 
      printf("----closing %p\n",stmt); 
      sqlite3_finalize(stmt); 
      printf("----closed\n"); 
      stmt = NULL; 
     } 
    } 
    inline bool isOpen() { return stmt != NULL; } 
    inline ~SQLiteResultSet() { close(); } 
}; 

class SQLiteGateway 
{ 
private: 
    sqlite3 *db; 
    SQLiteResultSet *rs; 

    void resetResultSet(); 

public: 
    SQLiteGateway(char *dbName); 
    SQLiteResultSet *select(char *query); 
    bool tableExists(char *tableName); 
    void execute(char *query); 
public: 
    ~SQLiteGateway(void); 
}; 

SQLiteGateway::SQLiteGateway(char *dbName) 
{ 
    db = NULL; 
    rs = NULL; 
    int error = sqlite3_open(dbName, &db); 
    if (error) 
    { 
     throw sqlite3_errmsg(db); 
    } 
} 

void SQLiteGateway::resetResultSet() 
{ 
    if(rs != NULL) 
    { 
     delete rs; 
     rs = NULL; 
    } 
} 

SQLiteResultSet *SQLiteGateway::select(char *query) 
{ 
    sqlite3_stmt *res; 
    const char  *tail; 

    resetResultSet(); 

    int error = sqlite3_prepare_v2(db,query,-1,&res,&tail); 
    if (error != SQLITE_OK || res == NULL) 
    { 
     throw sqlite3_errmsg(db); 
    } 

    rs = new SQLiteResultSet(res); 
    return rs; 
} 

SQLiteGateway::~SQLiteGateway(void) 
{ 
    resetResultSet(); 

    if(db != NULL) 
    { 
     sqlite3_close (db); 
    } 
} 

編輯:測試程序!

該查詢應該返回至少1行(我放棄其餘),並將一個整數作爲第一列。在我的機器中,第三次迭代崩潰。

注意:從SQLiteGateway的聲明中刪除tableExists()和execute(),我沒有在這裏複製它們的實體,也沒有在這個測試中使用它們。

int main() 
{ 
    SQLiteGateway *sqlg = NULL; 
    try 
    { 
     sqlg = new SQLiteGateway("./apptest.db"); 

     for(int i=0; i<5; i++) 
     { 
      SQLiteResultSet *rs = sqlg->select("select x from mytable"); 
      if(rs->next()) printf("%d\n", rs->intColumn(0)); 
      delete rs; 
     } 
    } 
    catch(char *str) 
    { 
     printf("Abnormal termination: %s\n", str); 
    } 
    if(sqlg != NULL) 
    { 
     delete sqlg; 
    } 
} 

這裏是我與測試得到輸出(0是讀的價值,它不是在第3次迭代顯示 - 關閉/關閉線前/後敲定)。

0 
----closing 0048D1E0 
----closed 
0 
----closing 0048D1E0 
----closed 
----closing 0048DFF8 

我仍然一無所知,但我發現,在本次測試中,如果我取代由它做什麼直列使用SQLiteGateway的,沒有崩潰!地址總是一樣的。

+0

你可以發佈測試計劃嗎?理想情況下,只需一個最小的'main'函數即可調用SQLite函數導致崩潰。 – jalf 2010-07-27 13:22:12

+0

我可以,當我今晚回到家時......至少從現在開始8個小時:(感謝 – 2010-07-27 13:38:29

+0

順便說一下,在你的博客中有關RAII的很好的文章!我的SQLiteResultSet類不會初始化它的資源,但會獲得它的所有權, SQLiteGateway忘記了它 – 2010-07-27 15:13:52

回答

0

我發現了這個錯誤:這是我如何管理SQLiteResultSet對象的生命期!

SQLiteGateway持有生成的最後結果集的引用,如果它不爲空,則在下一個查詢之前將其刪除。但是,我可能已經在其他地方刪除了它,這實際上就是我正在做的!結果集對象不再存在,但SQLiteGateway對象不知道,它仍然有一個非空指針。

我得重新設計解決方案。

我沒有與指針工作多年,它顯示!感謝您的關注!

編輯:容易解決。我只是放棄了這個參考。調用SQLiteGateway :: select()的人擁有生成的SQLiteResultSet對象並負責刪除它。