I have a massive problem with SQLite in my iPhone app, that needs to be fixed for a client soon! Basically i'm pulling a column from a database and loading it into a table view on viewWillAppear. It works fine, for the first few times the view is loaded but then suddenly it starts return empty (null) values. Upon inspection it appears that there is an issue with opening the database maybe but this is my first SQLite project so it's hard to figure out what's going on.
Here is the code I use to pull the SQL information:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
NSLog(#"INCOME CALLED 1");
NavTabAppDelegate *appDelegate = (NavTabAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.incomeArray = [[NSMutableArray alloc] init];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSLog(#"INCOME CALLED 2");
const char *sql = "select IncomeID, IncomeName from Income ORDER BY IncomeName asc";
//const char *sql2 = "select categoryID, Size from coffee";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
NSLog(#"INCOME CALLED 3");
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSLog(#"INCOME CALLED 4");
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Income *incomeObj = [[Income alloc] initWithPrimaryKey:primaryKey];
//This is how I pull info from the database using the above select statement and setting it in the coffeeObj property of Coffee class
incomeObj.incomeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
//coffeeObj.coffeeSize = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
incomeObj.isDirty = NO;
[appDelegate.incomeArray addObject:incomeObj];
[incomeObj release];
NSLog(#"INCOME OBJECTS %#", incomeObj.incomeName);
//NSLog(#"CALLED");
}
}
}
else{
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory
NSLog(#"INCOME CALLED 5");
}
NSLog(#"INCOME CALLED 6");
}
I am calling the code in viewWillAppear as follows:
[Income getInitialDataToDisplay:[appDelegate getDBPath]];
Also this is the output from my console when the error occurs:
2011-06-17 12:21:48.307 CashCal[318:707] GET DB PATH CALLED
2011-06-17 12:21:48.310 CashCal[318:707] /var/mobile/Applications/2BD7CA1D-C7AB-4425-B5C1-974C4F4D057C/Documents/SQL.sqlite
2011-06-17 12:21:48.312 CashCal[318:707] INCOME CALLED 1
2011-06-17 12:21:48.314 CashCal[318:707] INCOME CALLED 5
2011-06-17 12:21:48.318 CashCal[318:707] INCOME CALLED 6
It appears the problem is with the first if statement opening the database. I really need help on this one
You should always destroy prepared statements by calling sqlite3_finalize(your_stmt). And you should always close youre connection not only if it sqlite3_open() failed.
Also do something like this:
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
//your code here
} else {
//print error
NSLog(#"sql-error: %s", sqlite3_errmsg(database));
}
for clarification, here a interface for your db, that should avoid opening the db when its allready open, and makes code cleaner
DBi.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#interface DBi : NSObject {
sqlite3* db;
}
- (void)opendDB;
- (void)closeDB;
- (NSArray*)getIcons;
#end
DBi.m
#import "DBi.h"
#import "Income.h"
static DBi *sharedDBi = nil;
#implementation DBi
- (void)dealloc {
sqlite3_close(db);
[super dealloc];
}
+ (DBi*)sharedManager {
if (sharedDBi == nil) {
sharedDBi = [[super allocWithZone:NULL] init];
[sharedDBi opendDB];
}
return sharedDBi;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedManager] retain];
}
- (void)opendDB {
NSString *dbPath = [[NSBundle mainBundle]pathForResource:#"path_to_db_file"ofType:#"sqlite"];
//open the database
if(!sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK) {
NSLog(#"connection to db failed");
sqlite3_close(db);
}
}
- (void)closeDB {
sqlite3_close(db);
}
- (NSArray*)getIncoms {
NSMutableArray rArray = [[[NSMutableArray alloc] init] autorelease];
const char *sql = "select IncomeID, IncomeName from Income ORDER BY IncomeName asc";
//const char *sql2 = "select categoryID, Size from coffee";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(db, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Income *incomeObj = [[Income alloc] initWithPrimaryKey:primaryKey];
incomeObj.incomeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
incomeObj.isDirty = NO;
[rArray addObject:incomeObj];
[incomeObj release];
}
} else {
NSLog(#"sql-error in getIncoms: %s", sqlite3_errmsg(db));
}
sqlite3_finalize(selectstmt);
return rArray;
}
Now you could do something like that.
DBi *dbi = [[DBi alloc] init];
NSArray *incoms = [dbi getIncoms];
[dbi release];
hope, that should help
use this code
sqlite3 *database;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
////your Code
if(sqlite3_prepare_v2(database, [sqlStatement cStringUsingEncoding:NSUTF8StringEncoding], -1, &compiledStatement, NULL) == SQLITE_OK)
{
NSLog(#"%#",sqlStatement);
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
//your code
}
}
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
Use FMDB for you sqlite tasks. It's a bad practice to use sqlite calls everywhere in the application. Just search on google for FMDB Example you will find one. FMDB is much easier to implement then SQLIte because FMDB handles all the things.
It turns out I was calling the same SQL method in two different places in very quick succession before the statements had a chance to be finalised each time. I guess this caused some sort of overlap eventually leading to the statements becoming invalid. Thanks for all the help. I will definitely be using CoreData or FMDB next time.
Related
I am working with database.When i am updating my fields for the first time database is updating.But when i try to update for the next time its not updating but it showing database is updating.Can i know where the problem is.Thanks!
-(void) updateData {
sqlite3_stmt *statement;
details = [updateArray objectAtIndex:0];
NSString *destinationPath = [self getDestinationPath];
const char *dbpath = [destinationPath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *updateSQL = [NSString stringWithFormat: #"UPDATE BirthdayRemainderList SET FirstName=\"%#\", LastName=\"%#\",Dob=\"%#\",Address=\"%#\",City=\"%#\",State=\"%#\",Email=\"%#\",Phone=\"%#\" WHERE ids=%#",details.firstNameString,details.lastNameString,details.dobString,details.addressString,details.cityString,details.stateString,details.emailString,details.PhoneString,details.ids];
const char *insert_stmt = [updateSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Row updated");
}
else {
NSLog(#"Failed to update row");
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
}
My Update Code goes here
-(IBAction) update
{
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
savedData = [[DetailsClass alloc] init];
savedData.firstNameString = firstName.text;
savedData.lastNameString = lastName.text;
NSLog(#"last name is %#",savedData.lastNameString);
savedData.dobString = dob.text;
savedData.addressString = address.text;
savedData.cityString = city.text ;
NSLog(#"Changed city value is %#",savedData.cityString);
savedData.stateString = state.text;
savedData.emailString = email.text;
NSLog(#"Changes state value is %#",savedData.emailString);
savedData.PhoneString = phone.text;
NSLog(#"Phone no is %#",savedData.PhoneString);
savedData.ids = idString;
NSLog(#"details in id %#",savedData.ids);
[delegate.updateArray addObject:savedData];
[delegate updateData];
}
Will this make any mistake
If we use two different arrays for saving and updating it will make any difference?
Let me know where the Problem is.Thanks!
Add sqlite error message to get more insight of the real problem.
sqlite3_prepare_v2(database, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Row updated");
}
else {
NSLog(#"Failed to update row %s", sqlite3_errmsg(database));
}
If sql db is busy somehow it would happen. Make sure you close the db if you have opened in some where else like firefox's addon.
Can anybody help me out why this piece of code is leaking and how can we handle it?
sqlite3 *database;
if (pickerList) {
self.pickerList=nil;
[pickerList release];
}
self.pickerList=[[NSMutableArray alloc] init];
NSString *dbPath = [self applicationDocumentsDirectory];
dbPath=[dbPath stringByAppendingPathComponent:#"database"];
dbPath=[dbPath stringByAppendingPathComponent:#"OFFENDERSDB.sqlite"];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
if (isAlertForViolationPicker) {
const char *sqlStatement = "SELECT * FROM VIOLATIONS_TBL";
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while (sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *recSTR=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
[self.pickerList addObject:recSTR];
[recSTR release];
recSTR=nil;
}
}
//[tempRowArray release];
sqlite3_finalize(compiledStatement);
//sqlite3_reset(compiledStatement);
sqlite3_close(database);
}
else {
const char *sqlStatement = "SELECT * FROM PLAN_TBL";
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while (sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *recSTR=[[NSString alloc] initWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
[self.pickerList addObject:recSTR];
[recSTR release];
recSTR=nil;
}
}
sqlite3_finalize(compiledStatement);
sqlite3_close(database);
}
}
sqlite3_reset(compiledStatement);
recSTR is leaking in this case and I have tried all the below mentioned solutions but none worked (updated the code)
Thanx in advance
It looks as though you may be leaking pickerList. You have a pointer to pickerList which you then set to nil. Then you send a release message to this point (which is in effect a no-op). If you use:
if (pickerList)
{
[pickerList release];
self.pickerList=nil;
}
instead of your current code, do you fare any better? Without seeing more code it's hard to say, but you definitely want to release before you set the ivar to nil. (This said if you you've done #property (retain) UIPickerList *pickerList then self.pickerList = nil will release pickerList. If you've done this then your [pickerList release] call is redundant.)
You may well get a report of a recSTR leaking from instruments. But that doesn't mean that the issue isn't with pickerList. Looking at the code, it would not unlikely that recSTR is owned by an instance of pickerList that is hanging around because you've discarded the pointer to it and then sent a release message to nil. So you'll end up with a leak of recSTR and pickerList.
I'm reading data from a sqlite database however in my detailed view one of the data items is not showing correctly. It show what looks like random memory i.e. UIDeviceRGB...(I have two strings, one that works one that doesn't). I can't work out why but when I run the debug it returns null.
Here is my code in HydratedDetailData:
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
double add = sqlite3_column_double(detailStmt, 2);
NSString *address = [NSString stringWithFormat:#"%#", add];
self.ClubAddress = address;
[address release];
}
Any help work be greatly appreciated!
code from Clubs.m
#import "Clubs.h"
static sqlite3 *database = nil;
static sqlite3_stmt *detailStmt = nil;
#implementation Clubs
#synthesize clubID, clubName, ClubAddress, isDirty, isDetailViewHydrated;
+(void) getInitialDataToDisplay:(NSString *)dbPath {
SQLAppDelegate *appDelegate = (SQLAppDelegate *) [[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select clubID, clubName from clubNames";
sqlite3_stmt *selectstmt;
if (sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while (sqlite3_step(selectstmt)== SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Clubs *clubObj = [[Clubs alloc] initWithPrimaryKey:primaryKey];
clubObj.clubName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
clubObj.isDirty = NO;
[appDelegate.clubArray addObject:clubObj];
[clubObj release];
}
}
}
else
sqlite3_close(database);
}
+(void) finalizeStatements {
if (database) sqlite3_close(database);
}
-(id) initWithPrimaryKey:(NSInteger) pk {
[super init];
clubID = pk;
isDetailViewHydrated = NO;
return self;
}
-(void) hydrateDetailViewData {
if (isDetailViewHydrated) return;
if (detailStmt == nil) {
const char *sql = "Select ClubAddress from clubNames Where clubID = ?";
if (sqlite3_prepare_v2(database, sql, -1, &detailStmt, NULL) !=SQLITE_OK)
NSAssert1(0, #"Error while creating detail view statment. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_int(detailStmt, 1, clubID);
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
double add = sqlite3_column_double(detailStmt, 0);
NSString *address = [NSString stringWithFormat:#"%d", add];
self.ClubAddress = address;
}
else
NSAssert1(0, #"Error while getting the address of club. '%s'", sqlite3_errmsg(database));
sqlite3_reset(detailStmt);
isDetailViewHydrated = YES;
}
-(void) dealloc {
[ClubAddress release];
[clubName release];
[super dealloc];
}
#end
i had such a problem for reading double values, some of values read right but some read as 0 ! i have tried many things but at last my problem was solved by defining variables globally. i have defined the locally in .m file but the solution was to define them in .h file.i hope it can solve yours too.
good luck
Perhaps if you used the %d formatter in your [NSString stringWithFormat:#"%#", add]; call.
(%# is generally for strings, %d is for doubles)
After our little discussion-athon, this should fix what ails you.
if (SQLITE_DONE != sqlite3_step(detailStmt)) {
char *db_text = sqlite3_column_text(detailStmt, 2);
NSString *address = [NSString stringWithUTF8String: db_text];
self.ClubAddress = address;
}
This will pull the text value from the database and convert it into an NSString*. (hopefully)....
small clarification is this possible to open two tables from one sqlite db in one iphone class??? but i can't open it please give me the solution i'm a beginner
here i tried coding
- (void)viewDidLoad {
[super viewDidLoad];
list = [[NSMutableArray alloc] init];
if ([material isEqualToString:#"Derlin"]) {
[self sel1];
}
else if([material isEqualToString:#"Helers"]){
[self sel2];
}
}
-(void)sel1 {
[self createEditableCopyOfDatabaseIfNeeded];
NSLog(#"numberOfRowsInSection");
sqlite3_stmt *statement = nil; // create a statement
const char *sql = "Select * from material"; //create a query to display in the tableView
if(sqlite3_open([writableDBPath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"sqlite3_open");
if(sqlite3_prepare_v2(database, sql,-1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0,#"Error Preparing Statement",sqlite3_errcode(database));
else
{
while(sqlite3_step(statement) == SQLITE_ROW) // if the connection exists return the row of the query table
{
achemical = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,0)];
arates = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,1)];
anotes = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,2)];
Chemical * chemic = [[Chemical alloc] initWithName:achemical rates:arates notes:anotes];
[list addObject:chemic];
[chemic release];
}
}
}
sqlite3_finalize(statement);
}
-(void)sel2 {
[self createEditableCopyOfDatabaseIfNeeded];
NSLog(#"numberOfRowsInSection");
sqlite3_stmt *statement = nil; // create a statement
const char *sql = "Select * from material1"; //create a query to display in the tableView
if(sqlite3_open([writableDBPath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"sqlite3_open");
if(sqlite3_prepare_v2(database, sql,-1, &statement, NULL)!=SQLITE_OK)
NSAssert1(0,#"Error Preparing Statement",sqlite3_errcode(database));
else
{
while(sqlite3_step(statement) == SQLITE_ROW) // if the connection exists return the row of the query table
{
achemical = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,0)];
arates = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,1)];
anotes = [NSString stringWithFormat:#"%s",(char *)sqlite3_column_text(statement,2)];
Chemical * chemic = [[Chemical alloc] initWithName:achemical rates:arates notes:anotes];
[list addObject:chemic];
[chemic release];
}
}
}
sqlite3_finalize(statement);
}
thanks in advance
Sure, it's easy. Download FMDB, add it to your project, and add two FMResultSet objects in your class, one for each (query against each) table.
I am using this in a method:
+ (void) getA:(NSString *)dbPath {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select a from a_table";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.aString = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,0)];
coffeeObj.isDirty = NO;
[appDelegate.aArray addObject:coffeeObj.aString];
[coffeeObj release];
}
}
}
else
sqlite3_close(database);
}
When db has a row, it works fine, but when it has no rows, it just crashes.
What i need to know is, how can I handle my code so that it should work properly when there is no row in DB.
What should I add/modify in my code to behave properly, when there is no rows in the db?
Regards
I know this is a very old question but just had this problem of myself and put a very easy method to solve this. Hope this might help other visitors
In Your code
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.aString = [self checkNullForSQLStatmentColoumn:sqlite3_column_text(selectstmt, 0)];
-(NSString *) checkNullForSQLStatmentColoumn : (const unsigned char *) coloumn{
if(coloumn)
{
return [NSString stringWithUTF8String:(const char *)coloumn];
}
return #"";
}
Try this: to check if something is not NSNull type
if((NSNull *)YourResource != [NSNull null])