Problem getting a BOOL from a SQLite DB - iphone

I'm writing an app using a SQLite db to store information. My db table includes 5 fields: gameID (the primary key, int), gameName (Varchar), isLocked, isHidden, and isFavorite (all bools). I've set up the db using SQLite Manager in FireFox.
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
FightingGamesGuideAppDelegate *appDelegate = (FightingGamesGuideAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
//My second thought is that this line is incorrect. (see below)
const char *sql = "select gameID, gameName, isLocked, isHidden, isFavorite from GameTable";
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);
Games *coffeeObj = [[Games alloc] initWithPrimaryKey:primaryKey];
//getGameName
coffeeObj.gameName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
//I think these three lines are where the problem is
coffeeObj.isLocked=(BOOL)sqlite3_column_int(selectstmt, 2);
coffeeObj.isHidden=(BOOL)sqlite3_column_int(selectstmt, 3);
coffeeObj.isFavorite=(BOOL)sqlite3_column_int(selectstmt, 4);
//This line is only to check if the data loaded properly
NSLog(#"%i, %i, %i",isLocked, isHidden, isFavorite);
[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
else{
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
}
My question: Is this how to get a bool from a SQLite db? Is there a better way, such as using an int as a bool instead? If my code is all correct, do you have any other suggestion for how I can do this?
I know that the code aside from those three lines (and the following line) work properly. In the db, I've randomly assigned the three bools to YES or NO. I've also tried 1 and 0, to no effect.
If the lines are correct, my second thought is that line 9 is wrong. I modified it from "const ... ="select gameID, gameName from GameTable", and gameID and gameName still work properly.
My third thought is that I'm not updating the db correctly. I won't go into that, besides to ask if there's a specific "save" button/list item. If I update a gameName, that shows in my app, suggesting the db saves automatically.
In looking through the other questions, I've only found suggestions that CoreData is a better idea. However, if I'm on the right track, I only need these three lines to work, and I'm done (with this part).
Aside-I know that nothing is done with the bool variables besides printing them to the console. That's my next challenge.
Thank you for your time!

In addition to p.campbell's answer (+1), you could recall your BOOL value like this if you are storing YES/NO in your DB:
coffeeObj.isLocked=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)] isEqualToString:#"YES"];
coffeeObj.isHidden=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)] isEqualToString:#"YES"];
coffeeObj.isFavorite=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)] isEqualToString:#"YES"];

The SQLite datatypes page has the answer you're looking for:
SQLite does not have a separate Boolean storage class. Instead, Boolean values are stored as integers 0 (false) and 1 (true).

Related

Multiple queries in SQLite for iOS

Need help here.
i managed to get the drinkName and categories working. http://dl.dropbox.com/u/418769/2.png
but i need to Distinct the categories and get a count value of the SQL database.
something like this http://dl.dropbox.com/u/418769/2.png and then this http://dl.dropbox.com/u/418769/3.png
how would i need to do to get it done ?
i need to run SELECT drinkID,drinkName from drinks,
SELECT DISTINCT categories from drinks &
count each categoies's row.. can it be done ?
this is my database looks like..http://dl.dropbox.com/u/418769/4.png
i'm following this http://mybankofknowledge.wordpress.com/2010/12/01/accessing-sqlite-from-iphone-table-view/
ok it kind of work.....i on this error
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
(void) getInitialDataToDisplay:(NSString *)dbPath {
DrinkTabsAndNavAppDelegate *appDelegate = (DrinkTabsAndNavAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT drinkID, drinkName FROM drinks";
//const char *sql = "SELECT drinkName, categories FROM drinks";
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);
Drink *drinkObj = [[Drink alloc] initWithPrimaryKey:primaryKey];
drinkObj.drinkName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)];
//drinkObj.categories = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
drinkObj.isDirty = NO;
[appDelegate.drinksArray addObject:drinkObj];
[drinkObj release];
}
}
} else sqlite3_close(database); //close db to release all memory
}
(void) getCategory:(NSString *)dbPath {
DrinkTabsAndNavAppDelegate *appDelegate = (DrinkTabsAndNavAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT DISTINCT category FROM drinks";
//const char *sql = "SELECT drinkName, categories FROM drinks";
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);
Drink *catObj = [[Drink alloc] initWithPrimaryKey:primaryKey];
catObj.category = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
//drinkObj.categories = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
NSLog(#"run here");
catObj.isDirty = NO;
[appDelegate.categoryArray addObject:catObj];
[catObj release];
}
}
} else sqlite3_close(database); //close db to release all memory
}
If you want a count of the drinks in each category:
select category, count(drinkid) as DrinksInThisCategory
from drinks
group by category
But a properly normalized database would have a separate CATEGORIES table and you'd have a categoryid in your DRINKS table.
There are two ways i can come up with, one is querying your db: SELECT drinkID FROM drinks where categories='catX' once per category, then count rows of each. I think this could be accomplished by using some SQLite code to return all the counts with a single query.
The other way is using you appDelegate.drinksArray. First order your query by categories, then you could use a predicate almost same as the one above and using FOR drink IN drinksArray (pseudo code here...) to count how many drinks have each category.
Performance wise, i think the best way is using sql to query both, drinks and count of each category, may be in two different queries o best in one.
I would probably go with an approach that used an NSMutableDictionary instead of and array as the main datastructure.
-(void) addDrink:(Drink) drink
{
//_drinks has been initialised earlier and is of type NSMutableDictionary
NSMutableArray categoryDrinks = [_drinks objectForKey:drink.categories];
if (categoryDrinks == nil)
{
categoryDrinks = [[[NSMutableArray alloc] init] autorelease];
[_drinks setObject:categoryDrinks forKey:drink.categories];
}
[categoryDrinks addObject:drink];
}
This mimics the structure that you show in your images, the number of items in each category can now be found by calling [[_drinks objectForKey:categoryName] count]. Look at the apple documentation for NSMutableDictionary and NSMutableArray
If you just want the numbers there is the SQL GROUP BY expression you can get to the category counts doing something like this
SELECT CategoryColName, COUNT(CategoryColName) FROM TableName GROUP BY CategoryColName
This will give you pairs with the name of your category and the count
One small advice, in your Drinks object you use the property categories. I always try to keep the plurality of the name in sync with the type of data. This means I only use the plural for something if it is a collection of sorts. Otherwise I use the singular, this can be extended to SQL columns, while the column contains the categories of all the drinks. It is still only one category per drink. You did name the other columns using the singular.

iPhone SQLITE Select statement with variables

I am creating an iOS app that reads data from a single SQLITE table using variables. I don't have a problem running my select statement when all variables are populated, but I want to eventually have a large amount of variables and allow the users to skip ones that they don't see as meaningful to them. In other words, how can I make this work even when variables are null or 0 such as ignoring that part of the select statement, but continuing on? I have tried to use IF statements or CASE statements, but then I get the undeclared error. I could repeat the entire getInitialDataToDisplay with IFs, but there has to be an easier way.
+ (void) getInitialDataToDisplay:(NSString *)dbPath{
int addOne = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion1key"];
int addTwo = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion2key"];
int addThree = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion3key"];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *querystring= [NSString stringWithFormat:#"select * from animalswhere description > %i and description < %i and cash >= %i",addOne, addTwo, addThree]; //WORKS FINE IF ALL VARIABLES HAVE VALUES, BUT DOES NOTHING IF VARIABLES ARE EMPTY
const char *sql = [querystring UTF8String];
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);
Animal *animal = [[Animal alloc] initWithPrimaryKey:primaryKey];
animal.name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
animal.description = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)];
animal.imageURL = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 5)];
animal.cash = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 32)];
[appDelegate.animals addObject:animal];
[animal release];
}
}
} else sqlite3_close(database);
}
Sounds like what you need is to use marker variables/values, and a little boolean logic:
SELECT valuea, valueb, valuec
FROM table
WHERE valuea = %inputParmA
AND (%inputParmB = 0 OR valueb = %inputParmB)
This will have the effect of selecting all rows where valuea matches the passed-in value (of inputParmA), and, if inputParmB is non-zero, valueb matches inputParmB.
You'll have to adapt this for your needs, but it's a quick-and-dirty alternative to using dynamic sql, if this isn't available (or difficult to generate).
Not completely sure about performance, but I have a query that has around a dozen of these, operating over a multi-million row database, and returns within a minute (hundreds of results).

Truncate table in SQLite3 on iPhone?

I want to completely clean all the contents in a SQLite3 table in my iPhone app. My MySQL experience told me that I should use TRUNCATE. But it seems that SQLite3 doesn't support TRUNCATE (at least I got an error when preparing statement when I use the sentence TRUNCATE my_table_Name).
So I turn to DELETE FROM. Two questions: 1) Will DELETE FROM clean my table in a thorough way? 2) I tried the following code and it worked. But I highly doubted that there are unnecessary or even wrong codes in it. Can anyone help me take a look at it?
Thanks in advance.
Di
-(void)deleteAllUser {
NSString *ns_sql = [NSString stringWithFormat:#"DELETE FROM %#", [Config USER_TABLE_NAME]];
const char *sql = [ns_sql cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *statement = nil;
sqlite3* db = ...; //get the database instance
if(sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK) {
return;
}
//Without this line, table is not modified
int code = sqlite3_step(statement);
if (code == SQLITE_ROW) {
//Do nothing here...
}
sqlite3_finalize(statement);
}
I think
if (code == SQLITE_ROW) {
//Do nothing here...
}
this code should be like
if (code == SQLITE_DONE) {
//Do nothing here...
}
If you want to get any message then you can return YES from here and NO from else(if you change return type to BOOL from void).This can help you in showing messaeges.

strange iphone sdk sqlite memory leak

I have a very strange memory leak problem, it seems that sqlite3_step is doing some nasty stuff :|
I spent almost 4 hours trying to fix this but no luck till now :(
Here it is the code:
[dbList removeAllObjects];
sqlite3_stmt *statement = nil;
const char *sql = "SELECT * FROM dbs ORDER by rowOrder;";
if (sqlite3_prepare_v2(dbHandler, sql, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
DatabaseEntry *entry = [[DatabaseEntry alloc] init];
entry.databaseID = sqlite3_column_int(statement, 0);
entry.databaseTitle = [NSString stringWithFormat:#"%s", (char *)sqlite3_column_text(statement, 1)];
entry.databaseProtected = sqlite3_column_int(statement, 3);
entry.databaseFileName = [NSString stringWithFormat:#"%s", (char *)sqlite3_column_text(statement, 2)];
entry.databaseOrder = sqlite3_column_double(statement, 4);
[dbList addObject:entry];
[entry release];
}
}
sqlite3_finalize(statement);
The problem seems to be with my query, if I remove the "ORDER by rowOrder" part, everything seems to be just fine, also I'm using sqlcipher, and I'm wondering if that might cause this leak ?!
Thanks a lot for your attention !!!
Update: Hey Andy, I was wrong. I started looking into this more closely after running through some scenarios in Leaks. It looks like a bad merge from the upstream SQLite source missed two pager cleanup calls. The issue caused page cache to remain allocated after the pager was closed. It probably wouldn't affect most programs, but I would still definitely recommend pulling down the latest source code that fixes the issue from GitHub at http://github.com/sjlombardo/sqlcipher

How to bind text value to sqlite3 database in 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.