Problem getting data from SQLite3 on iPhone - iphone

I've been trying to return data from a table after already having accessed two before it, but in this case it get's into the while statement but does not assign any values as everything is set to null.
The code is:
NSMutableArray *all_species = [[NSMutableArray alloc] init];
sqlite3 *db_species;
int dbrc_species;
Linnaeus_LiteAppDelegate *appDelegate = (Linnaeus_LiteAppDelegate*) [UIApplication sharedApplication].delegate;
const char* dbFilePathUTF8 = [appDelegate.dbFilePath UTF8String];
dbrc_species = sqlite3_open (dbFilePathUTF8, &db_species);
if (dbrc_species) {
return all_species;
}
sqlite3_stmt *dbps_species;
const char *queryStatement = "SELECT species_id, species_name, species_latin, species_genus FROM \
linnaeus_species;";
if (sqlite3_prepare_v2 (db_species, queryStatement, -1, &dbps_species, NULL) == SQLITE_OK) {
sqlite3_bind_int(dbps_species, 1, [the_species_id intValue]);
while (sqlite3_step(dbps_species) == SQLITE_ROW) {
Species *species = [[Species alloc] init];
NSLog(#"%#", sqlite3_column_int(dbps_species, 0));
[species setSpecies_id:[[NSNumber alloc] initWithInt:sqlite3_column_int(dbps_species, 0)]];
char *new_name = (char *) sqlite3_column_text(dbps_species, 1);
[species setSpecies_name:nil];
if (new_name != NULL) {
[species setSpecies_name:[NSString stringWithUTF8String:(char *) sqlite3_column_text(dbps_species, 1)]];
}
char *new_latin = (char *) sqlite3_column_text(dbps_species, 2);
[species setSpecies_latin:nil];
if (new_latin != NULL) {
[species setSpecies_latin:[NSString stringWithUTF8String:(char *) sqlite3_column_text(dbps_species, 2)]];
}
[species setSpecies_genus:[NSNumber numberWithInt:sqlite3_column_int(dbps_species, 3)]];
[species setEdited:0];
[all_species addObject:species];
[species release];
}
sqlite3_finalize(dbps_species);
}
else {
sqlite3_close(db_species);
}
I've also tried using NSLog(#"Data: %#", sqlite3_column_text(dbps_species, 1)); and it causes a EXC_BAD_ACCESS error which suggests it could be memory related but I can't see why.

NSLog(#"Data: %#", sqlite3_column_text(dbps_species, 1));
Will cause EXC_BAD_ACCESS because the result of sqlite3_column_text is a C string (char*), not an NSString*. To print C strings you need the %s format specifier:
NSLog(#"Data: %s", sqlite3_column_text(dbps_species, 1));
Also, don't waste time to call sqlite3_column_text twice, e.g.
char *new_name = (char *) sqlite3_column_text(dbps_species, 1);
[species setSpecies_name:nil];
if (new_name != NULL) {
[species setSpecies_name:[NSString stringWithUTF8String:new_name]];
}

You could also try using the FMDB classes. These make using sqlite a LOT easier.
http://gusmueller.com/blog/archives/2008/03/fmdb_for_iphone.html

Related

Save UISwitch state in to sqlite database and access the same for display

I have a form contains text fields,a textView and 2 switches.Now I am using sqlite database for saving data in to database.Every thing is fine with text fields,text view saving,retrieving and even displaying the saved.As we know we don't have any feature called check box,we need to make use of switch which behaves/acts as that of check box functionality.Now while saving the switch state in to the database.I am saving it as a string/text format only,i.e. check the state of switch,assign value as '1' for ON and '0' for OFF to an integer.Now you might get a bit confused how can I save the integer to string format,yes you are right!I am converting the int value in to number and finally number to string.Then I have formed the query and have inserted the values in to sqlite database table.
Here is the code snippet of it for clear understanding:
int smsValue = 0;
if ([smsSwitch isOn])
{
smsValue = 1;
}
else
{
smsValue = 0;
}
NSNumber *smsNum = [NSNumber numberWithInt:smsValue];
NSString *selectedVal = [smsNum stringValue];
The selectedVal is what we are going to pass as string while forming the query,I also have found out that the state is getting saved properly,i.e. the value is correct.
Now I have a requirement,in my form I have switch for sms,Initially the state of that switch is Off,during off state,the last 2 fields i.e. textField and textView are hidden.If the user selects it (i.e. state is ON),then both the fields are said to be open to enter the values.
Totally I have saved 3 reminders,out of which I have saved 2 with out sms and 1 with sms and both the fields(last) filled.Now to pass the data,or to keep track of record saved after filling the form I have used a model class,please see the following snippet for clear understanding...how I have passed values for other fields(textFields):
-(void)viewDidLoad
{
textField.text = reminderInstance.Name;
textField1.text = reminderInstance.Event;
textField2.text = reminderInstance.Date;
textField3.text = reminderInstance.numDays;
textField4.text = reminderInstance.remGroup;
[super viewDidLoad];
}
Here the reminderInstance is object of the model class holding values like name,event,date etc...Now last 2 fields i.e. textField5 and textView are tied or linked to state of sms switch.If it is selected we need to consider both the fields values and pass them for user to edit/make changes.If not we need not bother about the last 2 fields.
Here is the action for the switch:
-(IBAction)toggleEnabledForswitch:(id)sender
{
if(smsSwitch.on)
{
textField5.hidden = NO;
textView.hidden = NO;
}
else
{
textField5.hidden = YES;
textView.hidden = YES;
}
}
Now in save action,I am checking the state of the switch and saving the values accordingly,the following code snippet will show a clear picture:
-(IBAction)save:(id)sender
{
int smsValue = 0;
if ([smsSwitch isOn])
{
smsValue = 1;
}
else
{
smsValue = 0;
}
NSNumber *smsNum = [NSNumber numberWithInt:smsValue];
NSString *selectedVal = [smsNum stringValue];
reminderInstance.smsString = selectedVal;
sqlite3_stmt *statement = nil;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &remindersDB) == SQLITE_OK && textField.text != nil)
{
if (self.navigationItem.rightBarButtonItem.title == #"Save" && [textField.text length] !=0 && [textField1.text length] !=0 && [textField2.text length] !=0 && [textField3.text length] !=0 && [textField4.text length] !=0)
{
NSLog(#"am in the save loop");
if (smsValue == 0)
{
NSString *insertSQL = [NSString stringWithFormat:#"INSERT INTO reminders(name,event,date,bfr,grp,val,sms) VALUES (\"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\")", textField.text, textField1.text,textField2.text,textField3.text,textField4.text,isSelected,selectedVal];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(remindersDB, insert_stmt, -1, &statement, NULL);
}
else if (smsValue == 1)
{
NSString *insertSQL = [NSString stringWithFormat:#"INSERT INTO reminders(name,event,date,bfr,grp,val,sms,num,bod) VALUES (\"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\", \"%#\")", textField.text, textField1.text,textField2.text,textField3.text,textField4.text,isSelected,selectedVal,textField5.text,textView.text];
textField5.text = reminderInstance.number;
textView.text = reminderInstance.msgBody;
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(remindersDB, insert_stmt, -1, &statement, NULL);
}
if (sqlite3_step(statement) == SQLITE_DONE)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"\nReminder Saved" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil,nil];
[alert show];
[alert dismissWithClickedButtonIndex:0 animated:YES];
[alert release];
}
else
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"Reminder not saved" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
}
}
}
I took another view controller with table view where I have populated a table view to view all the records that are being saved and upon selection we take him/her to the controller page that contains form i.e. populated with all the values.So far I have been successful in passing all the values using:
1.The following code in form controller page:
-(id) initWithReminder:(ReminderClass *)aReminder
{
if ( (self=[super init]) )
{
self.reminderInstance = aReminder;
}
return self;
}
2.The following parts of code in display controller:
-(void)loadReminders
{
// setup the reminders array
self.array = [[NSMutableArray alloc] init];
//Retrieve the values of database
const char *dbpath = [self.databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &remindersDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:#"SELECT * FROM reminders"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(self.remindersDB ,query_stmt , -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
ReminderClass *loadedReminder = [[ReminderClass alloc] init];
loadedReminder.reminderID = sqlite3_column_int(statement, 0);
loadedReminder.Name = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)]autorelease];
loadedReminder.Event = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)]autorelease];
loadedReminder.Date = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 3)]autorelease];
loadedReminder.numDays = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 4)]autorelease];
loadedReminder.remGroup = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 5)]autorelease];
loadedReminder.selString = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 6)]autorelease];
loadedReminder.smsString = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 7)]autorelease];
NSLog(#"selected value = %#",loadedReminder.smsString);
loadedReminder.number = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 8)]autorelease];
loadedReminder.msgBody = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 9)]autorelease];
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc]init]autorelease];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *theDate = [dateFormat dateFromString:loadedReminder.Date];
NSString *stringDate = [dateFormat stringFromDate:theDate];
loadedReminder.Date = stringDate;
NSLog(#"Date = %#",loadedReminder.Date);
[self.array addObject:loadedReminder];
[loadedReminder release];
}
sqlite3_finalize(statement);
}
sqlite3_close(self.remindersDB);
}
}
Now what is happening is since I have the table view in display controller with 2 containing fields except that are linked to sms state and one with all the fields filled.When I select the view controller it is crashing at line "loadedReminder.number":
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSPlaceholderString initWithUTF8String:]: NULL cString'
when I remove loadedReminder.number and loadedReminder.msgBody,we can view the table(controller page) no crash problem what so ever...Upon selection of table view cell I am doing the following:
-(void)tableView:(UITableView *)atableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReminderClass *rem = [self.array objectAtIndex:indexPath.section];
// Instantiate your detail/editor view controller,
// and pass in the ReminderClass object to be edited.
ERAddReminderViewController *rdvc = [[[ERAddReminderViewController alloc]initWithReminder:rem]autorelease];
[self.navigationController pushViewController:rdvc animated:YES];
rdvc.navigationItem.rightBarButtonItem.title = #"Edit";
}
Now I can see all the values properly populated in fields,except the problem with switch.Because it is saving properly,but the switch is always in off state,the state set to default as I have already said during filling the form and saving it (if user selects it and fills the last 2 fields also) to save it(record).
Sorry for this huge description,just wanted to make it detailed,so that any one can understand what my issue is,I have made several searches.But unable to achieve the task.
Can any one please suggest me a way of how to save the switch state in database and then populate the same when loading records from database sqlite.
Thanks every one in advance :)
I found out the solution for it,i.e. use an NSString and assign string value i.e. the retrieved state values that were added to the array.Let me explain this in detail:
In Add Reminder page,take an NSString let's say currentState,now come back to where the saved reminders are viewed,take an array named "stateValues" and add the state values to that array i.e.:
loadedReminder.smsState = [[[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 7)]autorelease];
[stateValues addObject:loadedReminder.smsState];
Now in did select row at index path,where we are navigating to add reminder page for editing or modifying the existing values of reminder,do this:
-(void)tableView:(UITableView *)atableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ReminderClass *rem = [self.array objectAtIndex:indexPath.section];
NSLog(#" switch state in did : %#",[stateValues objectAtIndex:indexPath.section]);
ERAddReminderViewController *rdvc = [[[ERAddReminderViewController alloc]initWithReminder:rem]autorelease];
rdvc.currentState = [stateValues objectAtIndex:indexPath.section];
}
We have successfully set the values to current state,now get back to add reminder page to set the state of switch,i.e. in viewDidLoad method do the following:
-(void)viewDidLoad
{
if ([currentState isEqualToString:#"0"])
{
[smsSwitch setOn:NO animated:NO];
}
else if([currentState isEqualToString:#"1"])
{
[smsSwitch setOn:YES animated:YES];
}
}
That's it,we have now set the state of switch properly by proper access of values that were saved for all consecutive reminders.
Hope it helps.Thank you!

Sqlite3 Fails Unless I Compile Without Optimization But Only On iOS 3.1.3

I have a very odd problem. My sqlite use in my project which has been working for a while has now stopped working on Xcode 4 compiled code, but only on devices with iOS3.1.3. It fails to return records, but if I turn off code optimization it works perfectly.
Does anyone know what may be the cause of this or has experienced similar issues.
Thanks
Rael
// get word from database
NSString *query = [[NSString alloc] initWithFormat:#"select kana_word, kanji_word, word_type_name, word_subtype_name, custom_category_name, kana_index, dictionary_id from dictionary join word_type on word_type.word_type_id=dictionary.word_type_id join word_subtype on word_subtype.word_subtype_id=dictionary.word_subtype_id join custom_category on custom_category.custom_category_id=dictionary.custom_category_id where kana_index=%d", inKanaIndex];
sqlite3_stmt *statement;
NSLog(#"%#", query);
const char *cQuery = [query UTF8String];
if (sqlite3_prepare_v2(jmwDb, cQuery, -1, &statement, nil) != SQLITE_OK) {
char *errMessage;
errMessage = (char*)sqlite3_errmsg(jmwDb);
sqlite3_finalize(statement);
[query release];
return nil;
}
// build a word object
int kanji_key;
if (sqlite3_step(statement) == SQLITE_ROW) {
// create new word
self.kana = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)];
self.kanji = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
self.wordType = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 2)];
self.wordSubType = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 3)];
self.customCategory = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 4)];
self.kanaIndex = sqlite3_column_int(statement, 5);
kanji_key = sqlite3_column_int(statement, 6);
self.dictionaryId = kanji_key;
}
else {
sqlite3_finalize(statement);
[query release];
return nil;
}

Loading data from database into detailed view

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

Memory Leak 'Select Statement' iPhone

Hi after several hours searching i have found the function which gives me memory leaks. The leaks i get are:
Leaked Object#AddressSize Responsible Library Responsible Frame
NSCFString 2 < multiple > 32 Foundation
-[NSPlaceholderString initWithBytes:length:encoding:]
i cant see anything wrong with this code. any help would be gratefully received Dan
-(void)readItems {
if (!database) return; // earlier problems
// build select statement
if (!selStmt)
{
const char *sql = "SELECT * FROM Demotivate order by name asc;";
if (sqlite3_prepare_v2(database, sql, -1, &selStmt, NULL) != SQLITE_OK)
{
selStmt = nil;
}
}
if (!selStmt)
{
NSAssert1(0, #"Can't build SQL to read items [%s]", sqlite3_errmsg(database));
}
// loop reading items from list
[items removeAllObjects]; // clear list for rebuild
int ret;
while ((ret=sqlite3_step(selStmt))==SQLITE_ROW)
{ // get the fields from the record set and assign to item
// primary key
NSInteger n = sqlite3_column_int(selStmt, 0);
Item *item = [[Item alloc] initWithPrimaryKey:n]; // create item
// item name
char *s = (char *)sqlite3_column_text(selStmt, 1);
if (s==NULL) s = "";
item.name = [NSString stringWithUTF8String:(char *)s];
// quantity needed
item.howOften = sqlite3_column_int(selStmt, 3);
// noted
s = (char *)sqlite3_column_text(selStmt, 2);
if (s==NULL) s = "";
item.Cost = [NSString stringWithUTF8String:(char *)s];
s = (char *)sqlite3_column_text(selStmt, 4);
if (s==NULL) s = "";
item.income = [NSString stringWithUTF8String:(char *)s];
s = (char *)sqlite3_column_text(selStmt, 5);
if (s==NULL) s = "";
item.wage = [NSString stringWithUTF8String:(char *)s];
[items addObject:item]; // add to list
[item release];
// free item
}
sqlite3_reset(selStmt); // reset (unbind) statement
}
Probably you forgot to release item.name in Item's dealloc.

How to handle returned null from SQLite

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