Question

I'm having memory management related crashes when using SQLite. It only crashes once every 30 or so tries unless I enable Guard Malloc (a test mode) in Xcode, in which case it crashes the second time I prepare a statement, 100% of the time. I think it has to do with how I'm opening or using the database, but I can't find anything wrong, BUT I'm a newbie with SQLite. Is there anything I'm forgetting?

Wrapper function for opening:

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;
}

Wrapper function for sending commands:

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;
}

How I use my opening function:

//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);

How I use sqlite3_prepare_v2 in my non-main threads (and where it crashes):

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
}

The error I get and the part of the SQLite3 library that causes it:

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;
}

Whenever I use sqlite3_column_text, I copy the result immediately. I do not modify the result. In databaseCommand and databaseSelect, char* command is null-terminated and valid (I checked). Each thread uses its own database handle, each connected to the same database. However, in this test case, there is only one thread connected to the database at any given time.

If there really is nothing wrong here, I have to assume that I trampled the memory elsewhere in my program, and I can't find anything in the rest of the program that even looks a bit dangerous. Plus it's suspicious that SQLite is the one thing crashing every time.

Was it helpful?

Solution

The sqlite3_prepare_v2 documentation says:

The calling procedure is responsible for deleting the compiled SQL statement using sqlite3_finalize() after it has finished with it.

sqlite3_free() can be used only for raw memory allocated with sqlite3_alloc(), or when a function such as sqlite3_exec() is documented as requiring it.

OTHER TIPS

Since you are using multiple threads operating on same database just make sure you close and reopen the database from these thread after every operation. You should also try not to neglect the error condition and add the close statement there also as shown below.

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() alone just compiles the SQL but does not run it. Call sqlite3_step() on the compiled statement to run it, or use sqlite3_exec() that combines prepare+step+finalize into one function call. From here. Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top