Storing and retrieving data from sqlite database - iphone

I am building an app which contains a form in one view,in which the user fills all the fields and when he clicks the save button the data must be saved in to database and after navigating back,there's another view which, when entered, must show the saved data(event).
I have created a database and have gone through several sqlite3 tutorials;
I have done all other changes to my code according to my requirement. However, when I use this statement to check whether data is inserted in database:
SELECT * FROM reminders;
I am getting nothing and I am confused whether data is inserted or not.
How do I save it properly, and how do I retrieve data from database to use and display it in other view?

First you should create the sqlite3 database file (check this link), then you should include it into your project. Now to connect to it you can use the following code:
#pragma mark -
#pragma mark Create/Load Database
+ (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
BOOL success;
NSFileManager * fileManager = [NSFileManager defaultManager];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) {
return;
}
// The writable database does not exist, so copy the default to the appropriate location.
NSError * error;
NSString * defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"DATABASENAME.DB"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
+ (sqlite3 *)getDBConnection {
[DatabaseController createEditableCopyOfDatabaseIfNeeded];
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:#"DATABASENAME.DB"];
// Open the database. The database was prepared outside the application.
sqlite3 * newDBConnection;
if (sqlite3_open([path UTF8String], &newDBConnection) == SQLITE_OK) {
//NSLog(#"Database Successfully Opened :)");
} else {
//NSLog(#"Error in opening database :(");
}
return newDBConnection;
}
then to insert a record you can use this code:
+ (void)insertEvent:(Event *)newEvent {
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "INSERT INTO Event (Serial, Name, Date) VALUES (?, ?, ?)";
sqlite3_stmt * insert_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &insert_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// Error
sqlite3_close(connection);
return;
}
sqlite3_bind_int(insert_statement, 1, newEvent.Serial);
sqlite3_bind_text(insert_statement, 2, [newEvent.Name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(insert_statement, 3, [newEvent.Date timeIntervalSince1970]);
int statement_result = sqlite3_step(insert_statement);
if ((statement_result != SQLITE_DONE) && (statement_result != SQLITE_OK)) {
//Error
sqlite3_close(connection);
return;
}
sqlite3_finalize(insert_statement);
// Get the Id of the inserted event
int rowId = sqlite3_last_insert_rowid(connection);
newEvent.Id = rowId;
sqlite3_close(connection);
}
now to get an event:
+ (Event *)getEventById:(int)id {
Event * result = nil;
sqlite3 * connection = [DatabaseController getDBConnection];
const char * text = "SELECT * FROM Event WHERE Id = ?";
sqlite3_stmt * select_statement;
int prepare_result = sqlite3_prepare_v2(connection, text, -1, &select_statement, NULL);
if ((prepare_result != SQLITE_DONE) && (prepare_result != SQLITE_OK)) {
// error
sqlite3_close(connection);
return result;
}
sqlite3_bind_int(select_statement, 1, id);
if (sqlite3_step(select_statement) == SQLITE_ROW) {
result = [[[Event alloc] init] autorelease];
result.Id = sqlite3_column_int(select_statement, 0);
result.Serial = sqlite3_column_int(select_statement, 1);
result.Name = (((char *) sqlite3_column_text(select_statement, 2)) == NULL)? nil:[NSString stringWithUTF8String:((char *) sqlite3_column_text(select_statement, 2))];
result.Date = [NSDate dateWithTimeIntervalSince1970:sqlite3_column_double(select_statement, 3)];
}
sqlite3_finalize(select_statement);
sqlite3_close(connection);
return (result);
}

Here is a blog post that should get you pointed in the right direction, pretty useful to me so sharing it with you.:P
http://dblog.com.au/iphone-development-tutorials/iphone-sdk-tutorial-reading-data-from-a-sqlite-database/

you can check whether your data has been saved or not by checking the database table. Go to Users>your computer name>Library>Application support>iphone Simulator>4.3(your version of ios)>Application.. Then look for your Application,go to documents and open the sqlite file. Here you can see the data.

You should use FMDB to reduce the complexity of your code.
It is an Objective-C wrapper around SQLite.
FMDB on github

This code used for storing data and retriving data from sqlite data base
First you just add sqlite3 frame work after that write bellow code in objective-c
ViewController.h
#import <UIKit/UIKit.h>
#import "sqlite3.h"
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *firstName;
#property (weak, nonatomic) IBOutlet UITextField *lastName;
#property (weak, nonatomic) IBOutlet UITextField *state;
#property (weak, nonatomic) IBOutlet UITextField *mobileNum;
- (IBAction)saveButton:(id)sender;
- (IBAction)featchButton:(id)sender;
#property (weak, nonatomic) IBOutlet UILabel *label;
#property NSString *myDatabase;
#property sqlite3 *marksDB;
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSString *docsDir;
NSArray *dirPaths;
dirPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir=dirPaths[0];
_myDatabase=[[NSString alloc]initWithString:[docsDir stringByAppendingString:#"marks.db"]];
NSLog(#"My Data base %#",_myDatabase);
NSFileManager *fileMgr=[NSFileManager defaultManager];
if ([fileMgr fileExistsAtPath:_myDatabase]==NO)
{
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
char *errMsg;
const char *sql_stmt="CREATE TABLE IF NOT EXISTS MARKS(ID INTEGER PRIMARY KEY AUTOINCREMENT ,FIRST NAME TEXT,LAST NAME TEXT,STATE TEXT,MOBILE INTEGER )";
if (sqlite3_exec(_marksDB, sql_stmt, NULL, NULL, &errMsg)!=SQLITE_OK)
{
_label.text=#"Failed to create Table";
}
sqlite3_close(_marksDB);
}
else
{
_label.text=#"Failed to Create/Open Database";
}
}
}
- (IBAction)saveButton:(id)sender {
sqlite3_stmt *statement;
const char *dbpath=[_myDatabase UTF8String];
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *insertSQL=[NSString stringWithFormat:#"INSERT INTO MARKS(firstname,lastname,state,mobile )VALUES(\"%#\",\"%#\",\"%#\",\"%#\")",_firstName.text,_lastName.text,_state.text,_mobileNum.text ];
const char *insert_stmt=[insertSQL UTF8String];
sqlite3_prepare_v2(_marksDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement)==SQLITE_DONE)
{
_label.text=#"Contact Added";
_firstName.text=#"";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
else
{
_label.text=#"Failed to Add Contact";
}
sqlite3_finalize(statement);
sqlite3_close(_marksDB);
}
}
- (IBAction)featchButton:(id)sender {
const char *dbpath=[_myDatabase UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_marksDB)==SQLITE_OK)
{
NSString *query=[NSString stringWithFormat:#"SELECT firstname,lastname,state,mobile, FROM MARKS WHERE firstname=\"%#\"",_firstName.text];
const char *query_stmt=[query UTF8String];
if (sqlite3_prepare_v2(_marksDB, query_stmt, -1, &statement, NULL)==SQLITE_OK)
{
if (sqlite3_step(statement)==SQLITE_ROW)
{
NSString *first=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)];
_firstName.text=first;
NSString *lastName=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 1)];
_lastName.text=lastName;
NSString *state=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
_state.text=state;
NSString *mobile=[[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
_mobileNum.text=mobile;
_label.text=#"Match Found";
}
else
{
_label.text=#"Not Matched";
_lastName.text=#"";
_state.text=#"";
_mobileNum.text=#"";
}
sqlite3_finalize(statement);
}
sqlite3_close(_marksDB);
}
}
#end

Related

Sqlite doesn't work on actual device (on my iPhone)

I've written a simple app that uses Sqlite database. It works great on iPhone simulator but doesn't work on my iPhone.
-(NSString *) getFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,YES);
NSString *documentsDir=[paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"database.sql"];
}
-(void)openDatabase
{
//Open
if (sqlite3_open([[self getFilePath] UTF8String], &db) != SQLITE_OK ) {
sqlite3_close(db);
NSAssert(0, #"Database failed to open.");
}
}
Output on Xcode after launching app:
2013-03-07 02:12:16.525 SqliteWorkApp[464:907] *** Assertion failure in -[SqliteWorkAppViewController insertRecord], /Users/cmltkt/Objective-C Apps/SqliteWorkApp/SqliteWorkApp/SqliteWorkAppViewController.m:77
2013-03-07 02:12:16.529 SqliteWorkApp[464:907] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error updating table.'
*** First throw call stack:
(0x3398e2a3 0x3b82797f 0x3398e15d 0x34263ab7 0x19b3b 0x195bd 0x357b5595 0x357f5d79 0x357f1aed 0x358331e9 0x357f683f 0x357ee84b 0x35796c39 0x357966cd 0x3579611b 0x374885a3 0x374881d3 0x33963173 0x33963117 0x33961f99 0x338d4ebd 0x338d4d49 0x357ed485 0x357ea301 0x19147 0x3bc5eb20)
libc++abi.dylib: terminate called throwing an exception
insertRecord function:
-(void)insertRecord
{
NSString *sql = [NSString stringWithFormat:#"INSERT OR REPLACE INTO 'countries' ('name', 'flag') " "VALUES ('Sample Data','Sample Data')"];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err)
!= SQLITE_OK) {
sqlite3_close(db);
NSAssert(0, #"Error updating table.");
}
}
I had same problem,
I used SQLite Db portable file in app and It was working on simulator very well but not on real device.
So after digging a lot, I found, when I dragged sqlite db file into my project, Xcode did not add it to bundle resources.
Please! go this way
select your project go to "Build Phases"
add your database.sqlite (it should be. sqlite as i know) file to Bundle resources.
and For handling all SQLite stuff, my database helper class code is
#import "ASCODBHelper.h"
#import <sqlite3.h>
#implementation ASCODBHelper
static ASCODBHelper *db;
+(ASCODBHelper *)database{
if (db == nil) {
db = [[ASCODBHelper alloc] init];
}
return db;
}
- (id)init{
self = [super init];
if (self) {
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"IOSMeeting" ofType:#"sqlite"];
if (sqlite3_open([sqLiteDb UTF8String], &db) != SQLITE_OK) {
NSLog(#"Failed to open database!");
}
}
return self;
}
-(void)getPresentationDeatilById:(NSString *)presentationid andSessionId:(NSString *)sessionid{
NSInteger pId = [presentationid integerValue];
NSInteger sId = [sessionid integerValue];
NSString *queryString = [[NSString alloc] initWithFormat:#"SELECT distinct mediaID,mediaURL,meetingName,trackName FROM Media WHERE presentationID='%d' and sessionID ='%d'",pId,sId];
NSLog(#"query is : %#",queryString);
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(db, [queryString UTF8String], -1, &selectStatement, nil) == SQLITE_OK) {
while (sqlite3_step(selectStatement) == SQLITE_ROW) {
char *mediaid = (char *) sqlite3_column_text(selectStatement, 0);
char *mediaurl = (char *) sqlite3_column_text(selectStatement, 1);
char *meetingname = (char *) sqlite3_column_text(selectStatement, 2);
char *topicname = (char *) sqlite3_column_text(selectStatement, 3);
NSString *mediaID = [[NSString alloc] initWithUTF8String:mediaid];
NSString *mediaURL = [[NSString alloc] initWithUTF8String:mediaurl];
NSString *topicName = [[NSString alloc] initWithUTF8String:topicname];
NSString *meetingName = [[NSString alloc] initWithUTF8String:meetingname];
//you can log here results
}
sqlite3_finalize(selectStatement);
}
}
#end
and Here is the code how I am using this.
Just import
#import "ASCODBHelper.h"
and call our db helper method this way
[[ASCODBHelper database] getPresentationDeatilById:presentationId andSessionId:sessionId];
Let me know if need help in this.

Program received signal "EXC_Bad_Access" NSMutableArray

if I try and reun the code below I get an EXE_bad_access message on [categoryList count]
NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
NSLog(#"number of items is %#", [categoryList count]);
The class is below
#import "CategoryItem.h"
#import "SQLite.h"
#interface CategoryItem : NSObject {
NSInteger ID;
NSInteger SortOrder;
NSString *Name;
NSString *ShoppingImage;
}
#property (nonatomic, nonatomic) NSInteger SortOrder;
#property (nonatomic, retain) NSString * Name;
#property (nonatomic, retain) NSString * ShoppingImage;
#property (nonatomic, nonatomic) NSInteger ID;
- (id)initWithObject:(NSInteger)itemID;
-(NSMutableArray *)getAll;
#end
#implementation CategoryItem
#synthesize ShoppingImage;
#synthesize Name;
#synthesize ID;
#synthesize SortOrder;
- (id)initWithObject:(NSInteger)itemID {
if ((self = [super init])) {
sqlite3 *database;
// Open the database. The database was prepared outside the application.
if (sqlite3_open([[SQLite fullFilePath] UTF8String], &database) == SQLITE_OK) {
// Get the primary key for all books.
const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem WHERE ID =?";
sqlite3_stmt *statement;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// We "step" through the results - once for each row.
sqlite3_bind_int(statement, 1, itemID);
while (sqlite3_step(statement) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
self.ID = itemID;
self.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
self.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
self.SortOrder = sqlite3_column_int(statement, 3);
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
return self;
}
-(NSMutableArray*)getAll{
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
sqlite3 *database;
// Open the database. The database was prepared outside the application.
if (sqlite3_open([[SQLite fullFilePath] UTF8String], &database) == SQLITE_OK) {
// Get the primary key for all books.
const char *sql = "SELECT ID, Name, ShoppingImage, SortOrder FROM CategoryItem ORDER BY SortOrder";
sqlite3_stmt *statement;
// Preparing a statement compiles the SQL query into a byte-code program in the SQLite library.
// The third parameter is either the length of the SQL string or -1 to read up to the first null terminator.
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK)
{
// We "step" through the results - once for each row.
while (sqlite3_step(statement) == SQLITE_ROW)
{
// The second parameter indicates the column index into the result set.
CategoryItem *categoryItem = [[CategoryItem alloc] init];
categoryItem.ID = sqlite3_column_int(statement, 0);
categoryItem.Name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
categoryItem.ShoppingImage = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 2)];
categoryItem.SortOrder = sqlite3_column_int(statement, 3);
[listArray addObject:categoryItem];
[categoryItem release];
categoryItem = nil;
}
}else{
printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(database) );
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
//NSLog(#"this is the list array count %#", [listArray count]);
return listArray;
}
- (void)dealloc {
[super dealloc];
[Name release];
[ShoppingImage release];
}
#end
It doesn't seem right the way you create your CategoryItem. You are calling allocbut not any init... method. You may want to use the initWithObject method that you have provided in your implementation.
From Apple docs:
It takes two steps to create an object
using Objective-C. You must:
Dynamically allocate memory for the
new object
Initialize the newly
allocated memory to appropriate values
An object isn’t fully functional until
both steps have been completed. Each
step is accomplished by a separate
method but typically in a single line
of code:
id anObject = [[Rectangle alloc]
init];
EDIT:
Beyond the initialization problem, there seems to be a conceptual problem (pointed out by #Terry Wilcox):
Calling the method getAllon an instance does not seem to make sense and therefore should be defined as a class method instead:
+ (NSMutableArray*)getAll;
and should be called like this:
NSMutableArray *categoryList = [CategoryItem getAll];
EDIT 2:
Your log statement does not seem right either. [categoryList count]returns a NSUIntegerand you are trying to print an object with %#. Use %iinstead:
NSLog(#"number of items is %i", [categoryList count]);
This code:
NSMutableArray *categoryList = [[CategoryItem alloc] getAll];
doesn't make sense. If getAll is a class method on CategoryItem, then it should be defined as
+ (NSMutableArray*)getAll;
and you should call it as
NSMutableArray *categoryList = [CategoryItem getAll];
Then categoryList will be an array that you don't own, so you may want to retain it when you get it.

how to retrieve data from SQLite using iphone sdk?

i have a simple sqllite database with 2 columns and multiple rows. e.g.
Code1 1000
Code2 2000
Code3 3000
Code4 4000
Code5 5000
I want to add all the fields together e.g. code1,code2,code3,code4,code5 and return the total between them to a label in my interface builder. How could i do this using the iphone sdk?are there any tutorials out there? thanks for any help on this.
You'll find a great tutorial here which will cover both the UI and the database part: http://www.techotopia.com/index.php/An_Example_SQLite_based_iOS_4_iPhone_Application
In summary, it would be something like this:
In databaseViewController.h,
#import <UIKit/UIKit.h>
#import "/usr/include/sqlite3.h"
#interface databaseViewController : UIViewController {
UILabel *total;
NSString *databasePath;
sqlite3 *db;
}
#property (retain, nonatomic) IBOutlet UILabel *total;
- (IBAction) getTotal;
#end
In databaseViewController.m,
#import "databaseViewController.h"
#implementation databaseViewController
#synthesize total;
-(void) getTotal
{
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &db) == SQLITE_OK)
{
NSString *totalSQL = [NSString initWithUTF8String: #"SELECT SUM(field2) FROM MyTable"];
const char *total_stmt = [totalSQL UTF8String];
sqlite3_prepare_v2(db, total_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *totalField = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
total.text = totalField;
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
.
.
.
- (void)viewDidUnload {
self.total = nil;
}
- (void)dealloc {
[total release];
[super dealloc];
}
#end
Something like that... then you just call getTotal in viewDidLoad (or whenever you press a button).

iphone sqlite query crashes if no rows returned

I have the following function in my iPhone project which works great...unless the query returns nothing and then the app crashes. It is being a pain to debug with none of the breakpoints being activated at all!
I know this works as I pass in static stuff that is in the DB and it returns a value.
-(NSString *)getSomeText:(NSString *)toPass {
sqlite3 *database;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"sf.sqlite"];
int strLength = 0;
strLength = [toPass length];
if (strLength <3)
return #"Unknown";
NSString *MIDstr;
NSMutableString * toPass Copy = [NSMutableString stringWithString:toPass];
MIDstr = [toPassCopy substringWithRange:NSMakeRange(0, 3)];
// 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
NSString *BaseSQL = [NSString stringWithFormat:#"select * from MIDS where MID = '%#'",MIDstr];
NSLog(BaseSQL);
const char *sqlStatement = [BaseSQL UTF8String];
//NSLog(BaseSQL);
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) {
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *returnString = [NSString stringWithFormat:#"%#",aName];
return returnString;
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
A. if sqlite3_step does not return any rows, you crash because you have declared that you are returning a NSString, but when there are no rows you return nothing.
The caller will try to read a NSString from the stack and thus end up dereferencing garbage.
To quickly fix the problem, write:
sqlite3_close(database);
return nil;
}
and make sure the caller handles nil results.
B/ If you do have data, your code never gets to call sqlite3_finalize and sqlite3_close because you return early:
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
[..]
return returnString;
while (sqlite3_step(sqlstatement) == SQLITE_ROW )
{
//Your code goes here
}
sqlite3_finalize(sqlstatement);
sqlite3_close(databaseRefObj);
close the database and finalize your statement after the while loop this helped me out,

Accessing an SQLite DB for two separate queries on iPhone App Initialization

I was successfully accessing my database to get a list of cities on the App launch. I tried running a second query against it right afterward to get the list of States but all that happens is that my app blows up with no usable error in the console (simply says "Program received signal: EXEC_BAD_ACCESS" and nothing more).
Here is the code, I was hoping someone could potentially explain to me what I'm doing wrong:
-(void) initializeDatabase{
// The database is stored in the application bundle
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK){
[self initializeCities:database];
[self initializeStates:database];
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
}
-(void) initializeCities:(sqlite3 *)db {
NSMutableArray *cityArray = [[NSMutableArray alloc] init];
self.cities = cityArray;
[cityArray release];
// Get the primary key for all cities.
const char *sql = "SELECT id FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
int primaryKey = sqlite3_column_int(statement, 0);
City *city = [[City alloc] initWithPrimaryKey:primaryKey database:db];
[cities addObject:city];
[city release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
-(void) initializeStates:(sqlite3 *)db {
NSMutableArray *statesArray = [[NSMutableArray alloc] init];
self.states = statesArray;
[statesArray release];
// Get the primary key for all cities.
const char *sql = "SELECT DISTINCT state FROM my_table ORDER BY state";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK){
// We "step" through the results - once for each row
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state;
state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
}
I can't debug this code as the debugger never hits my breakpoints at all.
If I remove the initializeStates method the app works as expected (albiet without a list of states).
You are releasing "state" without having allocated it. Try something like this:
while (sqlite3_step(statement) == SQLITE_ROW){
NSString *state = [[NSString alloc] initWithCString:(char*)sqlite3_column_text(statement, 0) encoding:NSASCIIStringEncoding];
//state = (NSString *)sqlite3_column_text(statement, 0);
[states addObject:state];
[state release];
}
Update: add cast above to fix compiler warning
Your problem is this:
NSString *state = (NSString *)sqlite3_column_text(statement, 0);
According to the documentation, sqlite3_column_text() returns a char*, not an NSString*.
Edit: You wouldn't have had this problem if you'd have used a wrapper ;)