第二次SQLite崩溃我使用sqlite3_prepare_v2

时间:2014-05-07 03:12:23

标签: c sqlite prepared-statement exc-bad-access

使用SQLite时,我遇到了与内存管理相关的崩溃问题。除非我在Xcode中启用Guard Malloc(一种测试模式),否则它每30次左右只会崩溃一次,在这种情况下,它会在第二次100%的时间内准备语句时崩溃。我认为这与我如何打开或使用数据库有关,但我找不到任何错误,但我是SQLite的新手。有什么我忘记了吗?

打开包装功能:

int databaseConnect(sqlite3 **db){
    int rc = sqlite3_open_v2(dbURL, db, SQLITE_OPEN_READWRITE, NULL);
    if(rc!=SQLITE_OK){
        fprintf(stderr, "Can't open database! Error: %s\n", sqlite3_errmsg(*db));
        sqlite3_close_v2(*db);
        return(SQL_ERROR);
    }
    return NO_ERROR;
}

发送命令的包装函数:

int databaseCommand(char* command, sqlite3* db){
    char* error = NULL;
    int ret = sqlite3_exec(db, command, NULL, 0, &error);
    if (ret!=SQLITE_OK){
        printf("SQL command aborted. Error: %s\n", error);
        return SQL_ERROR; //EDIT: this will cause the database to close later
    }
    if (error) sqlite3_free(error);
    return NO_ERROR;
}

我如何使用我的开场功能:

//ONCE IN MAIN THREAD, BEFORE ANY OTHER THREADS:
sqlite3* db = NULL;
databaseConnect(&db);
//call databaseCommmand a few times while creating tables...
sqlite3_close_v2(db);

//ONCE PER THREAD IN OTHER THREADS:
sqlite3* db = NULL; databaseConnect(&db);

我如何在非主线程中使用sqlite3_prepare_v2(以及它崩溃的地方):

struct LinkedList* databaseSelect(char* command, sqlite3* db){
    sqlite3_stmt* stmt = NULL;
    int retval = retval = sqlite3_prepare_v2(db,command,(strlen(command))*sizeof(char),&stmt,NULL); //crashes here the second time I run it
    if(retval!=SQLITE_OK){
        printf("Selecting data from database failed! Error: %s\n", sqlite3_errmsg(db));
        sqlite3_free(stmt);
        return NULL; //EDIT: this will cause the database to close later
    }
    // Then the function does stuff involving sqlite3_column_text and sqlite3_column_int…
    sqlite3_free(stmt);
    // return the linked list result
}

我得到的错误以及导致它的SQLite3库的一部分:

EXC_BAD_ACCESS (code=1) in this part of sqlite3.c:
/*
** Create a new virtual database engine.
*/
SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
  Vdbe *p;
  p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
  if( p==0 ) return 0;
  p->db = db;
  if( db->pVdbe ){
    db->pVdbe->pPrev = p; //error is right here; db->pVdbe is pointing to invalid address
  }
  p->pNext = db->pVdbe;
  p->pPrev = 0;
  db->pVdbe = p;
  p->magic = VDBE_MAGIC_INIT;
  return p;
}

每当我使用sqlite3_column_text时,我会立即复制结果。我不修改结果。在databaseCommand和databaseSelect中,char *命令以null结尾且有效(我已选中)。每个线程使用自己的数据库句柄,每个句柄都连接到同一个数据库。但是,在此测试用例中,在任何给定时间只有一个线程连接到数据库。

如果这里确实没有任何问题,我必须假设我在程序的其他地方踩踏了内存,而我在程序的其余部分找不到任何甚至看起来有点危险的东西。另外,人们怀疑SQLite每次都会崩溃。

2 个答案:

答案 0 :(得分:2)

sqlite3_prepare_v2 documentation说:

  

调用过程负责在完成后使用sqlite3_finalize()删除已编译的SQL语句。

sqlite3_free()只能用于使用sqlite3_alloc()分配的原始内存,或者当sqlite3_exec()等函数记录为需要它时。

答案 1 :(得分:0)

由于您使用在同一数据库上运行的多个线程,因此请确保在每次操作后关闭并从这些线程重新打开数据库。您还应该尝试不要忽略错误条件,并在那里添加close语句,如下所示。

if(retval!=SQLITE_OK){
        printf("Selecting data from database failed! Error: %s\n", sqlite3_errmsg(db));
        sqlite3_free(stmt);
        sqlite3_close(your_db_ptr);
        ......

    }

sqlite3_prepare_v2()只是编译SQL但不运行它。在编译语句上调用sqlite3_step()来运行它,或者使用将prepare + step + finalize组合成一个函数调用的sqlite3_exec()。来自here. 希望这会有所帮助。