library routine called out of sequence - iphone

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:#""]

Related

To retrieve BLOB image from sqlite

The code for saving image:
NSData *imageData=UIImagePNGRepresentation(animalImage);
NSString *insertSQL=[NSString stringWithFormat:#"insert into AnimalsTable (name,propertyID,animalID,breed,mainBreed,dateofbirth,sex,notes,imageData) values(\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")",nameString,propertyString,animalidString,breedString,mainBreedString,dateString,sexString,notesString,imageData];
sqlite3_stmt *addStatement;
NSLog(#"%#",appDelegate.sqlFile);
const char *insert_stmt=[insertSQL UTF8String];
if (sqlite3_open([appDelegate.sqlFile UTF8String],&database)==SQLITE_OK) {
sqlite3_prepare_v2(database,insert_stmt,-1,&addStatement,NULL);
if (sqlite3_step(addStatement)==SQLITE_DONE) {
sqlite3_bind_blob(addStatement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
NSLog(#"Data saved");
}
else{
//NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(database));
NSLog(#"Some Error occured");
}
sqlite3_close(database);
For retrieving image:
NSString *select_sql=[NSString stringWithFormat:#"select name,animalID,imageData from AnimalsTable where mainBreed='%#' AND breed='%#'",mainString,subString];
const char *sql = [select_sql UTF8String];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSString *animalName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
NSString *animalid=[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSData *dataForCachedImage = [[NSData alloc] initWithBytes:sqlite3_column_blob(selectstmt, 2) length: sqlite3_column_bytes(selectstmt, 2)];
[animalNamesArray addObject:animalName];
[animalIDArray addObject:animalid];
[imageDataArray addObject:dataForCachedImage];
}
}
}
else
sqlite3_close(database);
In UITableView CellForIndexPath:
NSData *dataForCachedImage=[imageDataArray objectAtIndex:indexPath.row];
UIImage *dataImage=[[UIImage alloc] init];
dataImage=[UIImage imageWithData:dataForCachedImage];
cell.imageView.image=dataImage;
When I debug the code:
I get bytes for NSData=226381 bytes And for dataImage 0x0.
Please help me.
You need to properly bind your values to the query for it to work. When you set the query on the first line, the description of the data would be inserted and not the correct data. If you were to log the query you would likely see something like <0FFAEC32 ... 23F0E8D1>.
You then attempted to bind the blob properly later but since your query was not written correctly it had no effect.
//Although you try to bind a blob here it does not get bound
//to anything since you do not include a parameter template.
sqlite3_bind_blob(addStatement, 1, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
To fix this here is a minimal example:
//query does not need to be an NSString* but just a const char *
//replace all %# with ?
const char *sql = "INSERT INTO AnimalsTable(name, ..., imageData) values(?, ..., ?)";
if (sqlite3_open([appDelegate.sqlFile UTF8String],&database)==SQLITE_OK) {
sqlite3_prepare_v2(database,insert_stmt,-1,&addStatement,NULL);
//Bind all of the values here before you execute the statement
sqlite3_bind_text(addStatement, 1, [nameString UTF8String], -1, NULL);
...
sqlite3_bind_blob(addStatement, 9, [imageData bytes], [imageData length], SQLITE_TRANSIENT);
if (sqlite3_step(addStatement)==SQLITE_DONE) {
NSLog(#"Data saved");
}
...
//You must finalize your statement before closing the database
sqlite3_finalize(addStatement);
sqlite3_close(database);
Using binded values helps prevent inserting invalid data which could be a SQL injection attack and improves query performance for queries that are run often.
Note*: If you are going to be doing a lot of SQL consider using FMDB or even Core Data.

Why can't I update statement in SQLite?

Can anyone help me with this code snippet:
-(void) updateData:(NSString*)value1:(NSString*)value2
{
sqlite3* database;
databaseName = #"AppDB.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
int databaseReturnCode = sqlite3_open([databasePath UTF8String], &database);
if(databaseReturnCode == SQLITE_OK) {
sqlite3_stmt *updateStmt;
const char *sql = "update PersonalInfo Set FirstName = ?,LastName = ? Where id = '1'";
//sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL);
sqlite3_prepare(database, sql, -1, &updateStmt,nil);
sqlite3_bind_text(updateStmt, 1, [value1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(updateStmt, 2, [value2 UTF8String], -1, SQLITE_TRANSIENT);
printf( "Update PersonalInfo| error or not an error? : %s\n", sqlite3_errmsg(database) );
while(sqlite3_step(updateStmt) == SQLITE_ROW)
{
NSString *aFirstName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(updateStmt, 1)];
NSString *aLastName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(updateStmt, 2)];
ProfileInfo *profile = [[ProfileInfo alloc]initWithFirstName:(NSString*)aFirstName LastName:(NSString*)aLastName];
[personalInfo addObject:profile];
[profile release];
}
sqlite3_reset(updateStmt);
sqlite3_finalize(updateStmt);
}
}
sqlite3_close(database);
}
It doesn't enter the while loop.
But if I remove the while loop and
enclose the code in a try-catch block and I get this exception:
+[NSString stringWithUTF8String:]: NULL cString
An sqlite_step while-loop is inappropriate for an update statement.
From the documentation at sqlite.org:
sqlite3_step() This routine is used to evaluate a prepared statement
that has been previously created by the sqlite3_prepare() interface.
The statement is evaluated up to the point where the first row of
results are available. To advance to the second row of results, invoke
sqlite3_step() again. Continue invoking sqlite3_step() until the
statement is complete. Statements that do not return results (ex:
INSERT, UPDATE, or DELETE statements) run to completion on a single
call to sqlite3_step().
Check your return value of sqlite_step. It should be something like SQLITE_DONE rather than SQLITE_ROW.

Iphone/ipad : I got "a file is encrypted or is not a database" error while using the pragma key in sqlite cipher?

I am using ios5.0 and Xcode 4.2 and sqlite 3. I can create the database and table and also I can read and write in table.
But If i use the sqlcipher , then I got the error "a file is encrypted or is not a database" .
Please explain me, why i am getting these kind of error ?. I have attached the code with this. Please find it.. and THanks in advance.
-(void) readFromDatabase {
// Setup the database object
sqlite3 *database;
devicesArray = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from patient;";
sqlite3_stmt *compiledStatement;
// const char* key = [#"test" UTF8String];
// NSLog(#"Length %lu" , strlen(key));
// sqlite3_key(database, key, strlen(key));
sqlite3_exec(database, "PRAGMA KEY='test123';", NULL, NULL, NULL);
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
int returnCode = sqlite3_exec(database, (const char*) "SELECT count(*) FROM patient;", NULL, NULL, NULL);
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
NSLog(#"%d",returnCode); // the return code is 26 and getting the error
if (sqlite3_exec(database, (const char*) "SELECT count(*) FROM patient;", NULL, NULL, NULL) == SQLITE_OK) {
NSLog(#"Success");
} else {
NSLog(#"Failure");
}
returnCode = sqlite3_prepare_v2( database, sqlStatement, -1, &compiledStatement, nil);
printf( "could not prepare statemnt1: %s\n", sqlite3_errmsg(database) );
NSLog(#"%d",returnCode); //the return code is 26 and getting the error
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
devices *d = [[devices alloc] initWithName:aName description:aDescription url:aImageUrl];
[devicesArray addObject:d];
[devices release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Your code seems to be assuming that the database already exists. Are you trying to open an existing, non-encrypted database and then encrypt it using SQLCipher? If so, what you are doing will not work.
The sqlite3_key function does not encrypt an existing database. If you want to encrypt an existing database you'd either need to ATTACH a new encrypted database and move data between the two, as described here:
http://zetetic.net/blog/2009/12/29/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher/
Or, using the SQLCipher 2 you can use sqlcipher_export which provides an easy way to move data between databases.:
http://groups.google.com/group/sqlcipher/msg/76d5b03426419761

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?

Sqlite3 in iPhone gives Database Locked Exception

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).