Sqlite - while (sqlite3_step(statement) == SQLITE_ROW) is not executing - ios

I am trying to get the highest value in the Points column in my DB, however the sqlite_step statement never get executed.
This is the method with this in it.
+(NSMutableArray*)getMax {
[self databaseInit];
highScore *newMax = [[highScore alloc]init];
NSMutableArray *maxPointsArray = [[NSMutableArray alloc]init];
if (sqlite3_open(dbpath,&peopleDB)==SQLITE_OK)
{
NSString *selectMaxSQL = [NSString stringWithFormat:#"SELECT MAX POINTS FROM PEOPLE"];
if(sqlite3_prepare_v2(peopleDB, [selectMaxSQL UTF8String],-1,&statement, NULL)==SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
newMax.max = sqlite3_column_int(statement, 0);
}
[maxPointsArray addObject:newMax];
}
}
sqlite3_finalize(statement);
sqlite3_close(peopleDB);
return maxPointsArray;
}
If it makes a difference the databaseInit method is -
+(void)databaseInit {
//get documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths [0];
//create path to database file
databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"people.db"]];
dbpath = [databasePath UTF8String];
}
Any help with this would be hugely appreciated, let me know if any more code is needed.

It is not executed because your query is wrong.
Assuming that points is a field of your people table, the query should be:
SELECT MAX(points) FROM people

You have the immediate answer, but the long term solution is to log sqlite3_errmsg if any SQLite functions fail. The way your code is written, you have no way of knowing which of the calls failed and why.
Thus, you may want:
+(NSMutableArray*)getMax {
int rc;
[self databaseInit];
highScore *newMax = [[highScore alloc]init];
NSMutableArray *maxPointsArray = [[NSMutableArray alloc]init];
if ((rc = sqlite3_open(dbpath, &peopleDB)) == SQLITE_OK) {
const char *selectMaxSQL = "SELECT MAX(POINTS) FROM PEOPLE";
if(sqlite3_prepare_v2(peopleDB, selectMaxSQL, -1, &statement, NULL)==SQLITE_OK){
if ((rc = sqlite3_step(statement)) == SQLITE_ROW){
newMax.max = sqlite3_column_int(statement, 0);
[maxPointsArray addObject:newMax];
} else {
if (rc == SQLITE_DONE) {
NSLog(#"No record returned");
} else {
NSLog(#"Step failed %s (%d)", sqlite3_errmsg(peopleDB), rc);
}
}
sqlite3_finalize(statement);
} else {
NSLog(#"Prepare failed %s (%d)", sqlite3_errmsg(peopleDB), rc);
}
} else {
NSLog(#"Open failed %d", rc);
}
sqlite3_close(peopleDB);
return maxPointsArray;
}
If you had done this with your original SQL, it would have reported precisely where in the SQL it encountered the error. Furthermore, if you have any problems in the future, this sort of defensive programming will help diagnose problems more quickly.
It's also worth noting that it's generally recommended not to open and close the database with every database call, but rather to open the database only once. It's more efficient and people have reported problems when constantly opening and closing databases.

Related

SQLite - best practice for opening database

I was in the situation where I've declared sqlite3 variable globally to store database conection and opening database every time without closing it. This lead to memory leaks on my app. I was able to fix this by closing database before open it.
sqlite3 *dbObj;
-(BOOL)openDB
{
//opening database
if (sqlite3_open(dbpath, &dbObj) == SQLITE_OK)
{
NSLog(#"Database opened successfully .....");
return YES;
}
else
{
return NO;
}
}
-(NSArray)getAllTablesInDatabse:(NSString*)database
{
sqlite3_close(dbObj);
[self openDB];
}
This is first way I fixed it.
There's another way I was able to do the same,to check dbObj is nil/NULL in openDB function instead of closing and opening each time. The second way :
-(BOOL)openDB
{
if(dbObj == nil || dbObj == NULL || dbObj = 0)
{
//opening database
if (sqlite3_open(dbpath, &dbObj) == SQLITE_OK)
{
NSLog(#"Database opened successfully .....");
return YES;
}
else
{
return NO;
}
}
else
{
return YES;
}
}
-(NSArray)getAllTablesInDatabse:(NSString*)database
{
[self openDB];
}
Which is best way from performance and other perspective ?
1) It's a good practice to close database after completion of desired task. For example you are creating one table then do like,
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
self.databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"contacts.db"]];
sqlite3_stmt *statement;
if (sqlite3_open([self.databasePath UTF8String], &_contactDB) == SQLITE_OK) {
NSLog(#"database opened successfully");
NSString *query = #"create table if not exists Employee (id integer primary key, name text, surname text, roll text, salary integer)";
const char *queryStatement2 = [query UTF8String];
char *errorMessage;
if (sqlite3_exec(self.contactDB, queryStatement2, NULL, NULL, &errorMessage) == SQLITE_OK) {
if (!errorMessage) {
NSLog(#"table created sucessfully!");
}
else{
NSLog(#"error : %s",errorMessage);
}
}
}
sqlite3_close(self.contactDB);
So, You should close your database every time after use.
2) It is good to check before opening database that if it is open or not compare to close database every time before open.
3) If you close database every time then it will definitely open every time so there is no need to put any condition.

Memory allocation keeps increasing while querying sqlite database

+ (BOOL) isExistingTicket:(NSString *)TicketID{
int numrows=0;
sqlite3 *database;
NSString *dbPath = [documentsDirectory stringByAppendingPathComponent:dbName];
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
NSString *sqlString = [NSString stringWithFormat:#"select count(*) from tickets WHERE ticket_id = '%#' LIMIT 1",TicketID];
sqlite3_stmt *selectStatement;
int returnValue = sqlite3_prepare_v2(database, [sqlString UTF8String], -1, &selectStatement, NULL);
if (returnValue == SQLITE_OK)
{
if(sqlite3_step(selectStatement) == SQLITE_ROW)
{
numrows= sqlite3_column_int(selectStatement, 0);
}
}
sqlite3_finalize(selectStatement);
}
else
sqlite3_close(database);
if (numrows > 0) {
return YES;
}else{
return NO;
}
}
Data set of around 200 comparing is using this query in my app. App memory usage keeps increasing when this function is called several times. It consumes more than 25Mb and does not decrease after that. Why is this happening? Can you suggest any optimizations?
This code closes the database only when the sqlite3_open call failed.
Move the sqlite3_close call into the first branch of the if.

while (sqlite3_step(statement) == SQLITE_ROW) Loop is not working

I had debugged code but don't know why While statement is not executing . After putting NSlog I came to know it's mismatching the result. 101 and SQLITE_ROW is equal to 100.
But query has result. If I fetch Select * from table name its returning me result and while statement executes. If I fetch result
sql_stmt = [NSString stringWithFormat:#"SELECT * FROM DeviceType WHERE upper(TypeName) = upper('%#')", deviceType];
While statement never execute so please help. don't know where is the problem.
Here is the code:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:#"ClientDB.sqlite"];
NSString* sql_stmt = [self searchStringForLocalData];
BOOL found = NO;
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char* sql1 = [sql_stmt UTF8String];
sqlite3_stmt *statement;
NSNumber *typeCount;
typeCount = 0;
if (sqlite3_prepare_v2(database, sql1, -1, &statement, NULL) == SQLITE_OK)
{
NSLog(#" %d, %d ",sqlite3_step(statement),SQLITE_ROW);
while (sqlite3_step(statement) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
int primaryKey1 = sqlite3_column_int(statement, 0);
NSNumber *typeId = [[NSNumber alloc] initWithInt:primaryKey1];
NSString *typeName =[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSString* sql_stmt1 = [NSString stringWithFormat:#"SELECT COUNT(*) FROM Devices WHERE DeviceTypeId =%d",primaryKey1];
const char *sql1 = [sql_stmt1 UTF8String];
sqlite3_stmt *statement;
NSNumber *typeCount;
typeCount = 0;
As already mentioned in a comment, the code calls sqlite3_step once for the NSLog, and then a second time in the condition of the while loop.
If your query returns exactly one record, the first sqlite3_step call will step to that record, and the second sqlite3_step call will then report (correctly) that no further results are available.
Just remove that NSLog call.
Alternatively, call sqlite3_step only once, and use the same return value for both the logging and the loop:
int rc;
...
while (1) {
rc = sqlite3_step(statement);
NSLog(#" %d, %d ", rc, SQLITE_ROW);
if (rc != SQLITE_ROW)
break;
...
}
if (rc != SQLITE_DONE)
NSLog(#"error: %s", sqlite3_errmsg(database));
Try to use the following syntax
while(sqlite3_step(stmt)!=SQLITE_DONE)
{
//do ur processing
for(i=0;i<NO_OF_COLS;i++)
{
char * tmp1=sqlite3_column_text(stmt,i);
if(tmp1!=NULL)
{
//use the value
}
else
{
//use a default
}
}
}
It shows that your table is totally empty. You may want to check your insert whether it works correctly. If you're using sqlite3_prepare_v2 to prepare your statement with some ? marks, remember to execute your query after all. Simply like this:
if(sqlite3_step(stmt) == SQLITE_DONE) NSLog(#"INSERTED");
else NSLog(#"NO INSERTED");
Note: It will display error only if your query is wrong, but not your prepared statement was executed or not.

Sqlite database cannot read column

I have a DBManager wich is fetching data from database (sqlite file). All other queries are fine, but this one seems to be somehow not working
-(NSArray *)readCountries{
NSLog(#"[DBManager] readCountries");
NSMutableArray *countriesArray = [[NSMutableArray alloc] init];
//open db from users filesystem
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char* sql = "SELECT DISTINCT country FROM aed ORDER BY rowid";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
//loop through results
while (sqlite3_step(statement) == SQLITE_ROW) {
//read data from record
NSString *_country;
char* tmpCountry = (char*)sqlite3_column_text(statement, 1);
NSLog(#"tmpCountry = %#", [NSString stringWithUTF8String:tmpCountry]);
if (tmpCountry != NULL) {
_country = [NSString stringWithUTF8String:tmpCountry];
}else{
_country = #"n/a";
}
NSLog(#"country = %#", _country);
[countriesArray addObject:_country];
}
}
//finalize statement
sqlite3_finalize(statement);
}
//close database
sqlite3_close(database);
NSLog(#"[DBManager] countriesArray has %d objects", [countriesArray count]);
return (NSArray*)countriesArray;
}
All I get from logs, that my array has 5 objects, which is fine - but it souldn't be only "n/a"... any idea? Other queries are good, they mostly use sqlite3_column_text so I don't get it, why it's not working here - maybe a fresh eye will help.
This is a confusing inconsistency with the sqlite C-api. When using the sqlite3_column_xxx functions, the column index is 0-based. But with the sqlite3_bind_xxx functions, the column index is 1-based.
Change this:
char* tmpCountry = (char*)sqlite3_column_text(statement, 1);
to:
char* tmpCountry = (char*)sqlite3_column_text(statement, 0);
BTW - you should add else statements to your sqlite3_open and sqlite3_prepare calls. If they fail you can log the error using the sqlite3_errmsg function.

while(sqlite3_step(statement) == SQLITE_ROW) loops never execute

hi all
i have some problem in using sqlite in IOS. i have select data from database, and then i want to save that data in a variable. But when i use while(sqlite3_step(statement) == SQLITE_ROW) loop, the code never execute.
here is my code :
-(void)retrieveProjectNameFromDb:(NSString *)segmenId{
NSString *query;
NSString *nameProjectStr;
NSString *dbPath = [[NSBundle mainBundle] pathForResource:#"database" ofType:#"sqlite"];
if (sqlite3_open([dbPath UTF8String], &db) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(0, #"Database failed to open.");
}else {
query = [NSString stringWithFormat:#"SELECT remote_projectname, remote_base_uri FROM REMOTE_SETTING WHERE remote_settingid = '%#' ORDER BY remote_projectname", segmenId];
NSLog(#"query : %#", query);
}
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [query UTF8String], -1, &statement, nil)==SQLITE_OK){
NSLog(#"sqlite row : %d", SQLITE_ROW);
NSLog(#"sqlite 3 : %d", sqlite3_step(statement));
while (sqlite3_step(statement) == SQLITE_ROW) {
char *nameProject = (char *)sqlite3_column_text(statement, 0);
if (nameProject == nil) {
NSLog(#"UNNAMED");
}else {
NSLog(#"else");
nameProjectStr = [NSString stringWithUTF8String:nameProject];
}
projectName.text = nameProjectStr;
nameProject = nil;
}
NSLog(#"project name : %#", projectName.text);
sqlite3_close(db);
}
}
when i nslog the value, sqlite3_step(statement) always show 101 and sqlite_row always show 100. Why it can happen??
can somebody help me, please?
thank you
Regards
-risma-
As you mentioned sqlite3_step(statement) always show 101 which means sqlite3_step has finished executions hence it means your sql query is not returning any rows from database. I would recommend you to first check in database if any record exists in table REMOTE_SETTING for remote_settingid which you are referring.
For your reference I took following constant snippet from sqlite.org
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
Remove the NSLog entry in which you are having sqlite3_step(). Since you have your NSLog statement executing sqlite3_step(), the record is already stepped through here. Hence, your while loop won't execute as there are no more rows to step through.
I think this will surely help you. :)
From your description of the logging output I would guess that your query is returning an empty row set. However I can see two other problems here:
You discard the result of your first call to sqlite3_step, because you log it then immediately call sqlite3_step again. That means you will miss the first row.
You probably should not be closing the database after a single query. You should finalize the statement after the query, but only close the database when your app shuts down.
Try:
if(sqlite3_prepare_v2(db, [query UTF8String], -1, &statement, nil)==SQLITE_OK){
NSLog(#"sqlite row : %d", SQLITE_ROW);
int stepResult = sqlite3_step(statement);
NSLog(#"sqlite 3 : %d", stepResult);
while (stepResult == SQLITE_ROW) {
char *nameProject = (char *)sqlite3_column_text(statement, 0);
if (nameProject == nil) {
NSLog(#"UNNAMED");
}else {
NSLog(#"else");
nameProjectStr = [NSString stringWithUTF8String:nameProject];
}
projectName.text = nameProjectStr;
nameProject = nil;
stepResult = sqlite3_step(statement);
}
NSLog(#"project name : %#", projectName.text);
sqlite3_finalize(&statement);
}
Try with something like this..
sqlite3 *database1;
NSString *sqlStatement1 = #"";
if(sqlite3_open([databasePath UTF8String], &database1) == SQLITE_OK)
{
sqlStatement1 = [NSString stringWithString:#"SELECT uniqueid from shortlisted"];
sqlite3_stmt *compiledStatement1;
if(sqlite3_prepare_v2(database1, [sqlStatement1 cStringUsingEncoding:NSUTF8StringEncoding], -1, &compiledStatement1, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement1) == SQLITE_ROW)
{
NSString * str = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
}
}
sqlite3_finalize(compiledStatement1);
sqlite3_close(database1);
}
Also close database instance whenever you open them.. if you are not closing an instance then that can cause issues..
Sorry I am pasting this from my project.. but actually I am in a bit hurry... I'll modify this answer when I reach home...

Resources