Sqlite3 in iPhone gives Database Locked Exception - iphone

I am newbiee in iphone and sqlite and doing some tutorials.
I have created one method which stores some temperory informartion into my database.
Now when i am on firstView controller of my Application i called that method twice. It stores the data twice into the particular table. Now i went to SecondViewController and in there i have a new sqlite3 object in the header file and i again copy paste that method into the SecondViewController. Now when i call that method on Second ViewController it gives me following error:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while inserting data. 'database is locked''
my Code is:
-(void)storeTemp
{
SchoolFocusIPadAppDelegate *delegate = (SchoolFocusIPadAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"sfocusDB.sqlite"];
if(sqlite3_open([path UTF8String], &databases) == SQLITE_OK){
//const char *sql = "select * from animal";
const char *sql = "insert into groups(id, groupid, name, desc , createdon,createdby) Values(?,?,?,?,?,?)";
sqlite3_stmt *add;
if(sqlite3_prepare_v2(databases, sql, -1, &add, NULL) == SQLITE_OK){
NSLog(#"Connection Successful");
NSLog(#"***Storing START on Database ***");
sqlite3_bind_text(add, 2, [[NSString stringWithFormat:#"Temp Group Dont Open"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(add, 3, [[NSString stringWithFormat:#"kjhasd"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(add, 4, [[NSString stringWithFormat:#"asdas"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(add, 5, [[NSString stringWithFormat:#"asdsa"] UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(add, 6, [[NSString stringWithFormat:#""] UTF8String], -1, SQLITE_TRANSIENT);
NSLog(#"***Storing END on Database ***");
if(SQLITE_DONE != sqlite3_step(add))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(databases));
else {
NSLog(#"YES THE DATA HAS BEEN WRITTEN SUCCESSFULLY");
}
}
sqlite3_finalize(add);
}
sqlite3_close(databases);
}
Please Help me friends. M really stucked.
Thanks alot

here lots of chances to kill
1.if your id is not null in DB it will kill with constraint failed error
if you don't want to insert any value to id remove from list
like
const char *sql = "insert into groups( groupid, name, desc , createdon,createdby) Values(?,?,?,?,?)";
and change that number 1-5 instead of 2-6

I've had some problems with sqlite3_close following an if/else (not sure why) but when I also put the finalize and close calls inside the if, it'd work out. I started following every open and close with an NSLog of the same (numbered so I knew which one was which) and you can then see which step left the DB open (and therefore locked)

Have you copied the database to the writable doc-dir?
Your prepare-call needs a little change:
From if(sqlite3_prepare_v2(databases, sql, -1, &add, NULL) == SQLITE_OK){
To if(sqlite3_prepare_v2(databases, [sql UTF8String], -1, &add, NULL) == SQLITE_OK){
(but that's nit the reason for the database locked).

Related

UITableViewController not always consistent with actual SQLite database until after restarting app

In my "viewWillAppear" callback I attempt to populate a UITableViewController with data from SQLite database for the user to see.
However, what I noticed was if I switch to another tab, commit a new row of data into SQLite and switch back to the UITableViewController it does not update with the new row I just added to the database. I have to quit out of the app completely and navigate back to the UITableViewController in order to see the new row reflected on the table view.
How do I get around this problem (i.e. how do I force always showing the very latest information in SQLite on the UITableViewController after switching back and forth a bunch of times?)
Would appreciate all / any advice.
Here is the code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSLog(#"viewwillappear");
//[[self tableView] reloadData];
int rc=-1;
if (databasePath == nil) {
NSLog(#"database path is NIL. Trying to set it");
databaseName = #"mymemories.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
return;
}
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
memoriesArray = [[NSMutableArray alloc]init ];
sqlite3_stmt *statement = nil;
NSString *fullQuery = #"SELECT * FROM memories";
const char *sql = [fullQuery UTF8String];
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0, #"Error preparing statement '%s'", sqlite3_errmsg(database));
else
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
NSString *place= [NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 4)];
//[User setName:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]];
//[User setAge:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 2)]];
[memoriesArray addObject:place];
//[currentUser release];
}
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
}
Also in case this is relevant, here is the code that commits to the SQLite database:
NSData * blob = [NSData dataWithContentsOfURL:recordedTmpFile];
int rc=-1;
rc = sqlite3_open([databasePath UTF8String], &database);
if(rc == SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
NSLog(#"Connected To: %#",databasePath);
sqlite3_stmt *updStmt =nil;
const char *sql = "INSERT INTO memories (data,place) VALUES (?,?);";
rc = sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL);
if(rc!= SQLITE_OK)
{
NSLog(#"Error while creating update statement:%#", sqlite3_errmsg(database));
}
sqlite3_bind_text( updStmt, 2, [[tags text] UTF8String], -1, SQLITE_TRANSIENT);
rc = sqlite3_bind_blob(updStmt, 1, [blob bytes], [blob length] , SQLITE_BLOB);
if((rc = sqlite3_step(updStmt)) != SQLITE_DONE)
{
NSLog(#"Error while updating: %#", sqlite3_errmsg(database));
sqlite3_reset(updStmt);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
//rc = sqlite3_reset(updStmt);
sqlite3_close(database);
}
As an extension of an explanation of Darren's answer.
First off his answer is correct.
Secondly you need to use an NSMutableArray to ensure consistency, this is where you are going wrong by not updating it as you should
The steps you should be taking to ensure consistency are the following:
Loading Data into Table
In viewDidLoad, call your SQL statement and load it into your array
in viewWillAppear ensure that your array contains data, if not display a notice that no results were returned
Saving Data into Database
Update the change to the array (or datasource)
Update the Database with the updated datasource to ensure consistency
Update the table with one of the 4 UITableView reloading methods
Using the NSMArray to ensure consistency between updates and app loads is fairly common practise has be recommended to me in the past by fellow co workers with decades of experience.
Note:
You will need to synchronise the datasource to ensure that 1 thread is accessing it at any 1 time otherwise you will get a crash.
Assuming you read your SQL data into an array, then use this array to build the UITableView, when you add a record to your SQL database, you either need to also add it to the array used to build the table, or re-read the data from the database into the array.

Inserting array of value in Sqlite3 i-phone

I am trying to insert a set of values in an sqlite table using a for loop. It is inserting only one set of value. I am posting here my code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"myDatabase.sql"];
for(int i=0;i<[arr count];i++)
{
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"\n inserting data \n");
sqlite3_exec(database, [[NSString stringWithFormat:#"INSERT INTO AnswerConnect VALUES('%#')",[arr objectAtindex:i] ] UTF8String], NULL, NULL, NULL);
//sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
Thanks in advance.
You have to first prepare a sqlite statement to insert data in table.Try this :
sqlite3_stmt *statement = nil
const char *sql = "insert into tablename (col1,col2) Values( ?, ?)";
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"Error while creating add statement. '%s'", sqlite3_errmsg(database));
}
for(int i=0;i<[arr count];i++)
{
sqlite3_bind_text(statement, 1,[[arr objectAtindex:i] UTF8String] , -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(add_statement))
{
NSLog(#"Error while inserting result data. '%s'", sqlite3_errmsg(database));
}
//Reset the add statement.
sqlite3_reset(statement);
}
Don't do like that! Don't open/close SQLite connection in loop like that! Open handle to database outside from loop and than just use pointer on it. In this kind of request it's unsafe to insert format, because SQL statement may be compiled with some kind of injection code. Use sqlite3_stmt instead and bind values to it. Also if you compile only one instance of sqlite3_stmt and reuse it, this will give you better performance than compiling new statements all the time.
How many columns in each data set? Does it insert only one value from single data set like string?

library routine called out of sequence

I am trying to write into a DB in sqlite on the iphone , i want to enter 3 values into the DB . Given Below is my code but i am getting the following sqlite error message:
-(void) writeintoDatabase:(id)value1:(id)value2:(id)value3 {
sqlite3 *database;
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value1 UTF8String], -1, SQLITE_TRANSIENT);
NSInteger keyID;
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
}
Thanks mmcomb, Thanks Mat. From both your advices i have solved the problem completely.
Firstly I wasn't checking the DB path and then opening the
connection.
Secondly after having prepared the statement and bounding the
appropriate variables i had to call sqlite3_step before calling
sqlite3_last_insert_rowid.
Thirdly i needed to call sqlite3_finalize to free the resources.
Fourthly i needed to sqlite3_close the connection.
'library routine called out of sequence' error was due to not opening and closing the connection properly.
This is the right code:
NSInteger keyID; // in .h class
NSMutableArray *user;// in .h class
-(void) writeintoUserDatabase:(NSString*)value1:(NSString*)value2:(NSString*)value3
{
sqlite3* database;
databaseName = #"UserData";// create a db with this name and keep it in app resources folder
NSString *pathToDatabase;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
pathToDatabase = [documentsDir stringByAppendingPathComponent:databaseName];
int databaseReturnCode = sqlite3_open([pathToDatabase UTF8String], &database);
NSLog(#"databaseReturnCode %d",databaseReturnCode);
if(databaseReturnCode == SQLITE_OK) {
const char *sql = "insert into UserData(firstName,lastName,userName) Values(?, ?, ?)";
sqlite3_stmt *insert_statement;
sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL);
sqlite3_bind_text(insert_statement, 1, [value0 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 3, [value2 UTF8String], -1, SQLITE_TRANSIENT);
printf( "is there an error?: %s\n", sqlite3_errmsg(database) );
while(sqlite3_step(insert_statement) == SQLITE_ROW)
{
NSString *aFirstName = [NSString stringWithUTF8String: (char*)sqlite3_column_text(insert_statement, 1)];
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 2)];
NSString *aUserName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(insert_statement, 3)];
users = [[NSMutableArray alloc]initWithObjects:aFirstName,aLastName,aUserName,nil];
}
keyID = sqlite3_last_insert_rowid(database);
sqlite3_reset(insert_statement);
sqlite3_finalize(insert_statement);
}
sqlite3_close(database);
}
Your code is missing a call to the sqlite3_step. You need to call that after having prepared the statement and bound the appropriate variables, and before you call sqlite3_last_insert_rowid.
Check the documentation for sqlite3_step for details, and always check the error codes returned by this type of function.
Don't call sqlite3_errormsg if no error has occurred, its result is undefined in that case.
Finally, you need to call sqlite3_finalize on the statement if you're not keeping a handle to it somewhere. Otherwise you'll get resource leaks and eventually crash. sqlite3_reset does not free the resources, it just "cleans" the statement so you can re-run it with different bound values.
Before preparing your sqlite stament/query you need to open the connection to your database...
sqlite3* database;
NSString pathToDatabase = #"/blah/blah/database.db";
const char* pathToDatabaseUTF8 = [databasePath UTF8String];
databaseReturnCode = sqlite3_open(pathToDatabaseUTF8, &database);
See the sqlite documentation for the sqlite3_open method for more info.
Oh, and don't forget to close that connection...
int sqlite3_close(database);
In my case it was a the string contained some special characters like it was ( ' ) single colon i replaced it with (" ") a space and it start working. [[[items objectAtIndex:locBtn.tag]valueForKey:#"description"] stringByReplacingOccurrencesOfString:#"'" withString:#""]

SQLite iPhone - Insert Fails

I am trying to insert a value to a SQLite db, but everytime I try my program simply crashes with no error message at all.
Here is my code:
- (void) insertToDatabase:(NSString *) refName {
// The Database is stoed in the application bundle
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"staticdata.sqlite"];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK){
const char *sql = "Insert into usersDates(dateDescription) VALUES (?)";
sqlite3_stmt *init_statement;
sqlite3_bind_text(init_statement, 1, [refName UTF8String], -1, SQLITE_TRANSIENT);
if(!sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) == SQLITE_OK){
NSAssert1(0, #"Failed to insert to database file with message '%s'.", sqlite3_errmsg(database));
}
if(sqlite3_step(init_statement) != SQLITE_DONE ) {
NSLog( #"Error: %s", sqlite3_errmsg(database) );
} else {
NSLog( #"Insert into row id = %d", sqlite3_last_insert_rowid(database));
}
sqlite3_finalize(init_statement);
} else {
sqlite3_close(database);
NSAssert1(0, #"Failed to open database file with message '%s'.", sqlite3_errmsg(database));
}
}
The error seems to occur on the bind statement. I have confirmed the database is actually being opened, and refname is correctly being passed to my method.
Can anyone help? I would normally use core data, however this is a bug fix to an existing project, and I simply do not have the time to allocate to making the move to core data.
The order of your statements is incorrect. bind_() is used after prepare()
SQLite bind() documentation
The first argument to the sqlite3_bind_*() routines is always a pointer to the sqlite3_stmt object returned from sqlite3_prepare_v2() or its variants.
const char *sql = "Insert into usersDates(dateDescription) VALUES (?)";
sqlite3_stmt *init_statement;
if(!sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) == SQLITE_OK){
NSAssert1(0, #"Failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
sqlite3_bind_text(init_statement, 1, [refName UTF8String], -1, SQLITE_TRANSIENT);
sqlite_step(init_statement);

Difficulty deploying database with my iphone app

I have an application that needs a database. The application is running fine in the simulator. But when i try to deploy it onto the iphone then it gives me the error that 'no such table animal'. Where is the problem? I am providing the code for better understanding
(void)viewDidLoad
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"AnimalDatabase.sql"];
sqlite3 *database;
if(sqlite3_open([filePath UTF8String], &database) == SQLITE_OK)
{
const char *sqlStatement = "insert into animal (id, name) VALUES (?, ?)";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK)
{
//NSLog(filePath);
sqlite3_bind_int(compiledStatement, 1, 12);
//NSLog(#"A");
sqlite3_bind_text( compiledStatement, 2, [#"abc" UTF8String], -1, SQLITE_TRANSIENT);
//NSLog(#"B");
if(sqlite3_step(compiledStatement) != SQLITE_DONE )
{
//NSLog(#"C");
NSLog( #"Error: %s", sqlite3_errmsg(database) );
}
else
{
//NSLog(#"D");
NSLog( #"Insert into row id = %d", sqlite3_last_insert_rowid(database));
}
}
else
{
NSAssert1(0, #"Error while creating insert statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_finalize(compiledStatement);
}
else
{
NSLog(#"Error Occured");
}
sqlite3_close(database);
[super viewDidLoad];
}
I think you need to look at what is being transfered to your iPhone. I had issues with this, there are some strange issues around how the database is created or not created on the actual iPhone. I think what is happening in your case is that no database is being transfered, and then your call to
sqlite3_open
is actually creating the database so you don't receive an error until you call a select statement.
Check your documentation on where to place the db in your resources to ensure it is copied to your iPhone when building.
The most likely reason is that the AnimalDatabase.sql file doesn't exist so there is no Animal table to insert into.