How to bind text value to sqlite3 database in iphone - iphone

I am developing a app for a movie in which I need to fetch data from the database considering some constraints. It works perfectly on the first occasion, but when I try to fetch data 2nd time it throws a runtime exception( the app crashes).I have to bind 3 placeholders. 2 are text and 1 is integer type. Here's the code which I am using to fetch data from the database.
-(void) Data2
{
databaseName = #"Cinema1.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath =[documentsDir stringByAppendingPathComponent:databaseName];
[self checkAndCreateDatabase];
sqlite3 *database;
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
if(detailStmt == nil)
{
const char *sql = "Select PVR,Fame,Cinemax,Big from listing where UPPER(State) = UPPER(?) and UPPER(City) = UPPER(?) and ZIP = ?";
if(sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) == SQLITE_OK)
{
NSLog(#"Hiiiiiii");
sqlite3_bind_text(detailStmt, 1, [t1 UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(detailStmt, 2, [t2 UTF8String], -2, SQLITE_TRANSIENT);
sqlite3_bind_int(detailStmt, 3, t3);
if(SQLITE_DONE != sqlite3_step(detailStmt))
{
NSLog(#"Helllloooooo");
NSString *pvr= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 0)];
NSString *fame= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 1)];;
NSString *cinemax = [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 2)];
NSString *big= [NSString stringWithUTF8String:(char *)sqlite3_column_text(detailStmt, 3)];
pvr1 = pvr;
fame1 = fame;
cinemax1 = cinemax;
big1 = big;
NSLog(#"PVR %# Fame %# Cinemax %# Big %#",pvr1,fame1,cinemax1,big1);
}
}
sqlite3_finalize(detailStmt);
}
}
sqlite3_close(database);}
Can anyone help me with this.

You are using the sqlite3_prepare_v2() and sqlite3_finalize() wrong. You only prepare a statement once and then you can bind values to it as much as you'd like. When you're done you call sqlite3_reset(). When you're completely done with this statement and won't ever use it again (i.e. you quit the app), then you use finalize.
Your app crashes because you finalize the statement, but the pointer detailStmt still points to the position where your statement once was, and tries to access that region of the memory.
See also here: http://www.sqlite.org/c3ref/prepare.html

As Toby points out, the variables pvr, fame, cinemax, and big (and the reassigned pvr1, fame1, cinemax1, and big1) are autoreleased when returned from -stringWithUTF8String:, but you never retain them. Any access to these variables after this point will cause a crash.
You say that you are seeing a thrown exception. It might be helpful to know what that exception is, by examining the console output. Also, you can enable a breakpoint on objc_exception_throw in libobjc.A.dylib, then debug with breakpoints on, to find the exact line at which this exception occurs.

In my limited Iphone experience when something runs once and then crashes on the next iteration, generally you are releasing memory you shouldnt be or not releasing memory you should be. Try looking at your memory allocations for problems.

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.

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.

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

Database Application not work properly

In my application i open the database many time in one loop and then again i open the database it will not execute if (sqlite3_open([[self getDBPath] UTF8String], &database) == SQLITE_OK) this statement so my application doesnt work....
-(NSString *)getDBPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"BasicGreetings.sqlite"];
}
-(void) fillimage:(NSInteger)imgno
{
testAppDelegate *appDelg = (testAppDelegate *)[[UIApplication sharedApplication]delegate];
sqlite3 *database= nil;
if (sqlite3_open([[self getDBPath] UTF8String], &database) == SQLITE_OK)
{
const char *sql = "select setflag from Tblsetflag where imgNo = ?";
sqlite3_stmt *selectStmt = nil;
if (sqlite3_prepare_v2(database, sql, -1, &selectStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_int(selectStmt, 1,imgno);
while (sqlite3_step(selectStmt) == SQLITE_ROW)
{
appDelg.a = sqlite3_column_int(selectStmt, 0);
NSLog(#"%d",appDelg.a);
}
}
}
sqlite3_close(database);
}
Can you post the code to getDBPath, I suspect it can't find the file... check that value you return from that method (run in the simulator) and browse the simulator documents directory in Finder and verify the file exists.
Don't open and close your database on every pass through your -fillimage: method. You really should open it once at the start of your application (or when your application has become active from the background) and close it when your application exits or goes to the background.
You also never finalize your prepared statement, which may be leading to the sqlite3_close() failing. Again, you may want to create a prepared statement once, then reset it every time you run that method and finalize it before the SQLite database is closed.

iphone xcode sqlite3_open remote host

i try to get a connection my server, with the sqlite3_open command!
my question...is it possible to that? i got the following code...
// Get the path to the documents directory and append the databaseName
databaseName = #"AnimalDatabase.sql";
NSString *serverpath = #"http://localhost/app/";
databasePath = [serverpath stringByAppendingPathComponent:databaseName];
and then this here
-(void) readAnimalsFromDatabase {
// Setup the database object
sqlite3 *database;
// Init the animals Array
animals = [[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 animals";
sqlite3_stmt *compiledStatement;
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)];
// Create a new animal object with the data from the database
Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];
// Add the animal object to the animals Array
[animals addObject:animal];
[animal release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
any suggestion??
Where you get the idea that SQLite can open database from URL??
It can open files only (or, create temporary db in memory).
The answer is just NO, there are lot of mistakes in your code anyway. For example:
databaseName = #"AnimalDatabase.sql";
Where did you get this. iPhone is working with sqlite database, it has nothing in common with sql files:)