How i releasing the dataArray from following snippet,
+(NSMutableArray *)getData: (NSString *)dbPath
{
NSMutableArray *_dataArray = [[NSMutableArray alloc] init];
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT DISTINCT name FROM databaseTable"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &selectstmt, NULL) == SQLITE_OK)
{
while (sqlite3_step(selectstmt) == SQLITE_ROW)
{
[_dataArray addObject:[NSString stringWithFormat:#"%d", sqlite3_column_int(selectstmt, 0)]];
}
}
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
return _dataArray;
}
The above method gives me memory leak and it getting me serious problem in future working of application.
return [_dataArray autorelease];
If you want to returned retained objects you need to make that clear by following the naming conventions. the method should start with new, create or copy. Otherwise you should return a autoreleased object.
I don't see any obvious leaks in the code you posted. The function is returning a NSMutableArray that has been allocated, so the caller would be responsible for calling release at some later point. Or, you may choose to make this an autorelease object.
Also, you probably want to call sqlite3_close() only if sqlite3_open() succeeded (i.e., move sqlite3_close() to inside the first if statement). Same idea for sqlite3_finalize().
There is some one-time initialization that SQLite does implicitly, but you shouldn't need to worry about that. Check the docs for:
int sqlite3_initialize(void);
int sqlite3_shutdown(void);
What type of object(s) are being reported as leaks?
Related
I try to retrieve data from sqlite. Unfortunately the table is filled with null values.
The console shows FIRST ID RECUPERÉ : (null). Can you give me your opinions please?
This is the code:
NSString * statementID = [NSString stringWithFormat:#"SELECT id_message FROM messages;"];
const char * sql_stmt_id = [statementID UTF8String];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(contactDB, sql_stmt_id, -1, &compiledStatement, NULL) == SQLITE_OK)
{
[tableauMsgReceived removeAllObjects];
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
[tableauMsgReceived addObject:[NSString stringWithFormat:#"%s",(char *) sqlite3_column_text(compiledStatement, 0)]];
NSLog(#"First ID : %#", [tableauMsgReceived objectAtIndex:0]);
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(contactDB);
The only way that the NSLog statement will give that result given your code is if tableauMsgReceived is nil.
Somewhere you need to do:
tableauMsgReceived = [[NSMutableArray alloc] init];
First of all, make sure you have done something like
tableauMsgReceived = [[NSMutableArray alloc] init];
Then, check whether database opened successfully
if (sqlite3_open([YourDBPath UTF8String], &yourDatabase) == SQLITE_OK){}
Last thing, maybe you can try
[tableauMsgReceived addObject:[NSString stringWithUTF8String:(const char *)sqlite3_column_text(statement, 0)]];
I have SQLite database with 5 columns named: Name, ID, ChildID, ParentID, BrotherID.
In this database I have many records and I want to store one of all columns value in array and return this array. For example I want to get all value in ParentID column. I use this query code:
Select ParentID from Table1 (Table1 is name of table)
This is my code for get array from certain column :
/*==================================================================
METHOD FOR GETTING MIDIFIED FROM DATABASE
==================================================================*/
- (NSMutableArray*)readingModified
{
ModiArray = [[NSMutableArray alloc] init];
// Setup the database object
sqlite3 *database2;
// Open the database from the users filessytem
if(sqlite3_open([[self DatabaseSqlite] UTF8String], &database2) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
//SQLIte Statement
NSString *sqlStatement_userInfo =[NSString stringWithFormat:#"Select ParentID from Table1"];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database2, [sqlStatement_userInfo UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Init the Data Dictionary
NSMutableDictionary *_dataDictionary2=[[NSMutableDictionary alloc] init];
NSString *_recordParentID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
[_dataDictionary2 setObject:[NSString stringWithFormat:#"%#",_recordModified] forKey:#"ParentID"];
[array addObject:_dataDictionary2];
}
}
else
{
NSLog(#"No Data Found");
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database2);
return ModiArray;
}
please tell me my mistake.
my friend a few be careful.
this code is right but in line you mistake :
[array addObject:_dataDictionary2];
instead array put ModiArray
/*==================================================================
METHOD FOR GETTING MIDIFIED FROM DATABASE
==================================================================*/
- (NSMutableArray*)readingModified
{
ModiArray = [[NSMutableArray alloc] init];
// Setup the database object
sqlite3 *database2;
// Open the database from the users filessytem
if(sqlite3_open([[self DatabaseSqlite] UTF8String], &database2) == SQLITE_OK)
{
// Setup the SQL Statement and compile it for faster access
//SQLIte Statement
NSString *sqlStatement_userInfo =[NSString stringWithFormat:#"Select ParentID from Table1"];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database2, [sqlStatement_userInfo UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Init the Data Dictionary
NSMutableDictionary *_dataDictionary2=[[NSMutableDictionary alloc] init];
NSString *_recordParentID = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
[_dataDictionary2 setObject:[NSString stringWithFormat:#"%#",_recordModified] forKey:#"ParentID"];
[ModiArray addObject:_dataDictionary2];
}
}
else
{
NSLog(#"No Data Found");
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database2);
return ModiArray;
}
You are adding object to array and allocating and returning ModiArray.
I to use sqlite database in my application. in my sqlite exist 10 records. I want read 4 data from this database and I want get this data until BroID in last data to be NULL (BroID is one of columns data) this is my code but I dont know how to use of loop in my code until BroID to be NULL.
-(NSMutableArray *)readInformationFromDatabase
{
array = [[NSMutableArray alloc] init];
// Setup the database object
sqlite3 *database;
// Open the database from the users filessytem
if(sqlite3_open([[self dataFilePath] UTF8String], &database) == SQLITE_OK)
{
// I want to use loop for certain work!!! (this work is get name from data base until BroID to be NULL )
NSString *sqlStatement_userInfo =[NSString stringWithFormat:#"Select * from table1"];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [sqlStatement_userInfo UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Init the Data Dictionary
NSMutableDictionary *_dataDictionary=[[NSMutableDictionary alloc] init];
NSString *_userName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
[_dataDictionary setObject:[NSString stringWithFormat:#"%#",_userName] forKey:#"Name"];
[array addObject:_dataDictionary];
}
}
else
{
NSLog(#"No Data Found");
}
please get me idea for done this code.
OK first the SELECT statement:
Change the statement from SELECT * ... to SELECT colname1, colname2, ... so you are then certain of the order in which columns are returned, and you won't have to refer to the schema in order to find out what order they come back in. This actually saves time.
BroID must be included in the columns being selected.
You'll want an ORDER BY clause in order to get consistent results.
You can probably get the database to only include rows WHERE BroID IS NOT NULL, which might suite your needs.
If you still need to use code to stop the fetching, then simply test for a NULL BroID column and break out of the while loop using:
while (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Fetch other columns
if (sqlite_column_type(compiledStatement, INDEX) == SQLITE_NULL)
break;
}
Where INDEX is the column index of BroID.
It's not clear if you want the row where BroID IS NULL in the result set; if you don't then perform the sqlite_column_type() test before fetching the columns, else leave it as above.
Refer to the reference for details.
I have an application where I access an SQLite database several times.. But, once I've accessed the database one time, all following attempts cause the app to crash...
I'm not sure if it's because the database has not been properly released...
An example, I run a search to populate a tableview with names of artists. Once I select an artist, I'm navigated to a new tableview, where I want to populate it with the artist's works.
But here's the problem. I access the database to populate the first view, but when I want to populate the second view, it doesn't enter sqlite3_prepare_v2 of the query... so this must mean the database is still in use by the old query..
So what is the proper way of handling closing a database after use?
Currently I do a query like this:
-(NSArray *)findAllArtists
{
NSMutableArray *returnArray = [[[NSMutableArray alloc] init] autorelease];
NSString *query = #"SELECT * FROM Painting GROUP BY Artist";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
char *uniqueIdChars = (char *) sqlite3_column_text(statement, 0);
char *artistChars = (char *) sqlite3_column_text(statement, 1);
NSString *uniqueId = [[NSString alloc] initWithUTF8String:uniqueIdChars];
NSString *artist = [[NSString alloc] initWithUTF8String:artistChars];
PaintingInfo *info = [[PaintingInfo alloc] initWithUniqueId:uniqueId artist:artist];
[returnArray addObject:info];
[uniqueId release];
[artist release];
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return returnArray;
}
You should look at fmdb wrapper at github. Even if you don't use it, look at the code.
Where are you opening the database? You're closing it in this code. Before you call it again, it needs to be open. You should consider just keeping it open for the duration of the single user iOS app and closing when you're done. What happens if you simply remove the close call?
The first thing you should do is check all your return codes for sqlite calls. for example, with step you're not handling anything other than SQLITE_ROW. At least log others. Also for finalize and close you're not handling or logging others.
Also, you're preparing (compiling) the sql statement but your not saving it off. prepare_v2 gives you back a compiled statement. Save it off as a member variable and call reset against it before using it again.
To answer your specific question of how to close - you need to consider that some statements may not have been finalized. Here's my close method: (BTW, ENDebug is my wrapper over NSLog)
- (void)close
{
if (_sqlite3)
{
ENInfo(#"closing");
[self clearStatementCache];
int rc = sqlite3_close(_sqlite3);
ENDebug(#"close rc=%d", rc);
if (rc == SQLITE_BUSY)
{
ENError(#"SQLITE_BUSY: not all statements cleanly finalized");
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0)
{
ENDebug(#"finalizing stmt");
sqlite3_finalize(stmt);
}
rc = sqlite3_close(_sqlite3);
}
if (rc != SQLITE_OK)
{
ENError(#"close not OK. rc=%d", rc);
}
_sqlite3 = NULL;
}
}
finally, consider adding much more logging along with the return codes so you can get more insight.
Hope that helps.
I'm trying to make an app, that reads from an SQLite3 database. I plan to pre-load data during development, so the app does not need to modify anything in the database, only read from it, make queries, etc.
What is the best practice for solely reading data? Should I open the database, read the data, and close it, with each query? The app will be making many small queries and a few large ones. Is it better to have the database open for the duration of the app, or open/close it with each fetch?
Reading:
1. For queries, it's important to re-use compiled statements.
2. Make sure you use parameters so you can re-use those compiled queries
When you call sqlite3_prepare_v2, it compiles the statement and gives you a reference to the statement back. Find a way to save that off and re-use it. See the code below for *statement. You pass &statement into prepare.
Also, note the use of ? for parameters. If you're going to re-use the statement, it's important to call sqlite3_reset() againt the statement, rebind the inputs from the program (parameters) and execute it again.
sqlite3_stmt *statement;
NSString *querySQL = #"update contacts set name=?,address=?,phone=? where id=?";
NSLog(#"query: %#", querySQL);
const char *query_stmt = [querySQL UTF8String];
// preparing a query compiles the query so it can be re-used.
// find a way to save off the *statement so you can re-use it.
sqlite3_prepare_v2(_contactDb, query_stmt, -1, &statement, NULL);
// use sqlite3_bind_xxx functions to bind in order values to the params
sqlite3_bind_text(statement, 1, [[contact name] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 2, [[contact address] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 3, [[contact phone] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_int64(statement, 4, [[contact id] longLongValue]);
Always check the return codes! and log or handle the errors.
rc = sqlite3_step(stmt);
switch (rc)
{
case SQLITE_ROW:
// ...
break;
case SQLITE_OK:
case SQLITE_DONE:
break;
default:
// ....
}
return NO;
}
if you get an error, log or get the eror message to provide more info:
- (NSString*)errorMessage
{
return [NSString stringWithCString:sqlite3_errmsg(_sqlite3) encoding:NSUTF8StringEncoding];
}
Use sqlite_open_v2 with the SQLITE_OPEN_READONLY flag. For example, I use the following method to open a database for reading only.
// Open for reading only.
- (int) openDatabaseAtPath:(NSString *) path
{
if (database != nil)
{
sqlite3_close(self.database);
[self setDatabase:nil];
}
int errorCode = SQLITE_OK;
errorCode = sqlite3_open_v2([path UTF8String],
&database,
SQLITE_OPEN_READONLY,
NULL);
return errorCode;
}
Unless you copy the database to the Documents directory, you work with the DB from the resource dir, and that one is readonly.
When you open the database using sqlite_open_v2 and the SQLITE_OPEN_READONLY flag, SQLite opens the file itself in read-only mode, so even if your application, due to a bug, corrupts memory belonging to SQLite, the database will stay untouched.
With this in mind, I'd keep the database open until the application quits. (You may wish to close it if you receive a low-memory notification and reopen it on demand, but opening and closing it for every query would be wasteful.)
As per you question you want to read data from database. So following are the answer of you questions.
No need to open db each time when you fire the query. Just one it one time. It is better if you create singleton class and open db in it for first time when it initiate.
Use following code method which will work for all select queries which has conditional select, group by etc.
I) it takes Output/result column names as input array ii ) TableName iii) Where condition iv)OrderBy clause v)group By clause
- (NSMutableArray *)runSelecteQueryForColumns: (NSArray *)p_columns ontableName: (NSString *)p_tableName withWhereClause: (NSString *)p_whereClause withOrderByClause: (NSString *)p_orederByCalause withGroupByClause: (NSString *)p_groupByClause
{
NSMutableArray *l_resultArray = [[NSMutableArray alloc] init];
if(!self.m_database)
{
if(![self openDatabase])
{
sqlite3_close(self.m_database);
//NSLog(#"error in select : DB creating : %#",p_whereClause);
return nil;
}
}
NSMutableString *l_simpleQuery =[[NSMutableString alloc] initWithString:#"Select"] ;
if(p_columns)
{
for(int l_row = 0 ; l_row < [p_columns count] ; l_row++)
{
if(l_row != [p_columns count]-1)
{
[l_simpleQuery appendString:[NSString stringWithFormat:#" %#,", [p_columns objectAtIndex:l_row]]];
}
else
{
[l_simpleQuery appendString:[NSString stringWithFormat:#" %#", [p_columns objectAtIndex:l_row]]];
}
}
}
else
{
[l_simpleQuery appendString:#" *"];
}
[l_simpleQuery appendString:[NSString stringWithFormat:#" From %#",p_tableName]];
if(p_whereClause)
{
[l_simpleQuery appendString:[NSString stringWithFormat:#" %#",p_whereClause]];
}
if(p_groupByCaluase)
{
[l_simpleQuery appendString:[NSString stringWithFormat:#" %#",p_groupByCaluase]];
}
if(p_orederByCalause)
{
[l_simpleQuery appendString:[NSString stringWithFormat:#" %#",p_orederByCalause]];
}
//NSLog(#"Select Query: - %#",l_simpleQuery);
const char *l_query_stmt = [l_simpleQuery UTF8String];
sqlite3_stmt *l_statement = nil;
int i = sqlite3_prepare_v2(self.m_database,
l_query_stmt, -1, &l_statement, NULL);
if (i == SQLITE_OK)
{
while(sqlite3_step(l_statement) == SQLITE_ROW)
{
[l_resultArray addObject:[self createDictionary:l_statement]];
}
sqlite3_finalize(l_statement);
}
else
{
sqlite3_finalize(l_statement);
//sqlite3_close(l_database);
DDLogError(#"%# - error in SQL :%#",THIS_FILE,l_simpleQuery);
return nil;
}
//NSLog(#"RESULT %#",l_resultArray);
return l_resultArray;
}