Странная ошибка с SQLite при завершении оператора - PullRequest
1 голос
/ 27 июля 2010

Я пишу тестовое приложение C ++ с SQLite, для которого я сделал небольшую оболочку.

Я несколько раз читаю с ним некоторые данные, и в четвертый раз, когда заканчиваю оператор, происходит сбой.Я уверен, что я сопоставляю каждое подготовленное утверждение с финализацией.В четвертый раз перед сбоем данные были успешно прочитаны.

Одна любопытная вещь: я теперь отображаю возвращенный указатель на оператор (через 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 строку (я отбрасываю остальные), ицелое число в качестве первого столбца.На моей машине происходит сбой 3-й итерации.

Примечание: удалите tableExists () и execute () из объявления SQLiteGateway, я не копировал их тела здесь, и они не используются в этом тесте.

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 на то, что он делает встроенным, никакого сбоя не будет !!И адрес всегда один и тот же.

1 Ответ

0 голосов
/ 28 июля 2010

Я нашел ошибку: это то, как я управляю временем жизни объекта SQLiteResultSet!

SQLiteGateway содержит ссылку на последний полученный набор результатов и, если он не равен нулю, удаляет его перед следующим запросом.Тем не менее, я мог удалить его уже где-то еще, что на самом деле я и делаю!Объект результирующего набора больше не существует, но объект SQLiteGateway не знает, у него все еще есть ненулевой указатель.

Мне нужно изменить решение.

Я не работал с указателями годами, и это видно!Спасибо за ваш интерес!

Редактировать: легко решить.Я просто прекратил держать эту ссылку.Тот, кто вызывает SQLiteGateway :: select (), владеет результирующим объектом SQLiteResultSet и отвечает за его удаление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...