Adding Batch values with an array - objective-c

I've trying to insert batch data from array in to Sqlite3 for my mobile project.
my code seems to be works fine, but it doesn't insert all data.
PS: I edited my code:
-(BOOL) saveBatchData:(NSArray *)userSourceID userDestID:(NSArray *)userDestID message:(NSArray *)message sentDate:(NSArray *)sentDate
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
for(int i=0; i<[message count]; i++)
{
NSString *insertSQL = [NSString stringWithFormat:#"insert into messages (userSourceID,userDestID, message, sentDate) values (\"%d\",\"%d\", \"%#\",\"%#\")",[[userSourceID objectAtIndex:i] integerValue],[[userDestID objectAtIndex:i] intValue], [message objectAtIndex:i],[sentDate objectAtIndex:i]];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_finalize(statement);
return YES;
} else {
NSLog(#"Hata = %s",sqlite3_errmsg(database));
return NO;
}
sqlite3_reset(statement);
}
sqlite3_close(database);
}
return NO;
}
The output seems to be , function just added few rows on 39 elements of array ( i think it's 4 ) . Can someone help me what's my mistake over here ?
Regards...

Your are preparing lots of statements, but execute only the last one.
Move the call to sqlite3_step into the loop.
Please note that you must call sqlite3_finalize for every statement you have prepared, regardless of whether the step suceeded or not.
You must not jump out of the function without calling sqlite3_close.
You must not call sqlite3_reset after the statement has been finalized.

Related

sqlite_prepare_v2 does not return SQLITE_OK

I have been trying to save highscore into database and have been failing for past week, and I have no clue why it is not working. I keep receiving "Problem with prepare statement" and refuses to insert info into database. I have checked with database manager to make sure there is not a typo with sql statement, and when query is run on manager, it works fine - it's just the iphone that's giving me the problem. If anyone could please look over quickly and see something wrong with it and could let me know, I would really appreciate it!
- (NSMutableArray *) saveLocal {
NSLog(#"save local database");
#try {
[self checkDB];
sqlite3_stmt *sqlStatement2;
NSString *sqlS = [NSString stringWithFormat:#"INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (%i,%i,\"%#\",%i,%i,%i,%i)",[[MySingleton sharedMySingleton] goDifficulty],[[MySingleton sharedMySingleton] goScore],_player, [[MySingleton sharedMySingleton] goPuzzles], [[MySingleton sharedMySingleton] goMultiplier], [[MySingleton sharedMySingleton] goOneshots], [[MySingleton sharedMySingleton] goHints]];
NSLog(#"%#",sqlS);
const char *sql = [sqlS UTF8String];
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
sqlite3_step(sqlStatement2);
sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement");
}
sqlite3_close(localHighscore);
}#catch (NSException *exception) {
NSLog(#"An exception occured: %#", [exception reason]);
}#finally{
NSLog(#"DB Loaded!");
}
}
and here is checkDB method which checks if database exists and creates one if it does not
- (void)checkDB {
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"localHighscore.sqlite"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO)
{
const char *dbpath = [databasePath UTF8String];
NSLog(#"file was not found");
if (sqlite3_open(dbpath, &localHighscore) == SQLITE_OK)
{
NSLog(#"db open");
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS localHighscore(pk INTEGER PRIMARY KEY AUTOINCREMENT, difficulty TINYINT, score MEDIUMINT, uname VARCHAR(255), puzzles TINYINT, multiplier TINYINT, oneshots TINYINT, hints TINYINT)";
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
sqlite3_close(localHighscore);
} else {
NSLog(#"Failed to open/create database");
}
}
[filemgr release];
}
Thanks in advance for the help!
A couple of thoughts:
You don't appear to call sqlite3_open before trying to use the database.
Whenever you get an error, you should look at sqlite3_errmsg, e.g.
if (sqlite3_exec(localHighscore, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table: %s", sqlite3_errmsg(localHighscore));
}
Probably unrelated to your problem, but you should generally not build a SQL statement using stringWithFormat (at least if you have any text fields). Use ? placeholders in your SQL and then use sqlite3_bind_xxx functions.
const char *sql = "INSERT INTO localHighscore (difficulty, score, uname, puzzles, multiplier, oneshots, hints) VALUES (?,?,?,?,?,?,?)";
if(sqlite3_prepare_v2(localHighscore, sql, -1, &sqlStatement2, NULL) == SQLITE_OK)
{
if (sqlite3_bind_int(sqlStatement2, 1, [[MySingleton sharedMySingleton] goDifficulty]) != SQLITE_OK) {
NSLog(#"bind 1 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_int(sqlStatement2, 2, [[MySingleton sharedMySingleton] goScore]) != SQLITE_OK) {
NSLog(#"bind 2 failed: %s", sqlite3_errmsg(localHighscore));
}
if (sqlite3_bind_text(sqlStatement2, 3, [_player UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"bind 3 failed: %s", sqlite3_errmsg(localHighscore));
}
// repeat this bind process for each variable
if (sqlite3_step(sqlStatement2) != SQLITE_DONE) {
NSLog(#"step failed: %s", sqlite3_errmsg(localHighscore));
}
// reset not needed (doesn't hurt, but not needed unless you're going to re-use it
// sqlite3_reset(sqlStatement2);
sqlite3_finalize(sqlStatement2);
NSLog(#"save complete");
} else {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(localHighscore));
}
sqlite3_close(localHighscore);
If you find this syntax unwieldy, then maybe consider using FMDB, which simplifies your SQL interaction. But be very wary of stringWithFormat with SQL (if the inserted string had a quotation mark, the sqlite3_prepare will fail, theoretically, your app is exposed to SQL injection attacks, etc.).
As an aside, you should not [filemgr release], as you don't own it.
I saw that the "sqlite3_prepare_v2 ()" function, returns a 'generic error' (error code = 1) when the SQL statement contains conditions like "booleanfield=false" instead of "booleanfield=0". The same SQL statement executed in the SQL box of SQLiteStudio program gives good results using indifferently the first or the second form of the comparison.

Using sqlite3_exec to do transactions in Objective-c and not having desired effect

Top of the morning to'ya!
I'm currently developing a iOS app in Objective-c and I have a local database that I allow the user to update over the internet.
The database can change rather drastically in only a few short days and thus could all have been reduced to a few lines where before there were hundred of thousands (unlikely but still).
I'm using sqlite and trying to perform a transactional delete and not getting any errors while running the following code, but it isn't having the desired effect i.e the lines that should be deleted aren't being removed!
I have ran select queries on the db before, while and after but always find the data that should've been deleted.
Here is the code
#try {
if(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK){
//Db exists and can be open
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
sqlite3_stmt *stmt;
const char *query = [[NSString stringWithFormat:#"DELETE FROM %#
WHERE ? = '?'", table] UTF8String];
if(sqlite3_prepare_v2(db, query, -1, &stmt, NULL)== SQLITE_OK)
{
//integer i = 0
//PrimaryKey contains k primaryKeys
for(int i=0; i<[primaryKey count]; i++){
//i = j < k
sqlite3_bind_text(stmt, 1, (const char *)[parameters UTF8String]
, [parameters lengthOfBytesUsingEncoding:NSUTF8StringEncoding], SQLITE_STATIC);
sqlite3_bind_text(stmt, 2,
(const char *)[[primaryKey objectAtIndex:i] UTF8String]
, [[primaryKey objectAtIndex:i] lengthOfBytesUsingEncoding:NSUTF8StringEncoding ]
, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_DONE){
NSLog(#"Delete commit failed. Error %s", sqlite3_errmsg(db));
return NO;
}
if(sqlite3_reset(stmt)!= SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
}
//i = k
}
if(sqlite3_exec(db, "COMMIT TRANSACTION", 0, 0, 0) != SQLITE_OK){
NSLog(#"SQL error %s", sqlite3_errmsg(db));
return NO;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return YES;
}
NSLog(#"SQL error %s", sqlite3_errmsg(db));
sqlite3_close(db);
return NO;
}
#catch (NSException *exception) {
NSLog(#"%#", [exception reason]);
return NO;
}
Any help or tips from those more used to using transactions in sqlite would be most appreciated.
You are not checking the result of
sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", 0, 0, 0);
There are extra quotes in
WHERE ? = '?'
You don't need those quotes with sqlite3_bind_text
I suspect you are trying to bind a column name for parameter 1. You cannot do this in SQLite. sqlite3_prepare_v2 needs to know the column names; your query is just doing a compare of the name of the column against parameter 2.

memory leak in sqlite3 in ios

Its location based application and application crash after 5-6
Minutes. When I run the app using performance tools, leaks, I keep
finding that all of my classes leak memory. i am calling this
function second by second. Please properly close my database or
sqlite3. Thanks.
- (void)locationUpdate:(CLLocation *)location {
for (int i=0; i<[arrayAlerts count]; i++) {
Msg=[DatabaseManager GetSelectedMessage:[NSString stringWithFormat:#"select text_msg from TextMsgTemplate where msg_id=%d",ObjAlertInfo.msgID]];
}
in Databasemanger
+(NSString *)GetSelectedMessage:(NSString *)SQL
{
NSString *msg;//=[[[NSString alloc]init] autorelease];
msg=nil;
sqlite3_stmt *dataset=nil;
dataset=[DatabaseManager getMessages:SQL];
while ((sqlite3_step(dataset)==SQLITE_ROW) ) {
msg=[NSString stringWithUTF8String:(char *)sqlite3_column_text(dataset, 0)];
}
sqlite3_finalize(dataset);
dataset = nil;
return msg;
}
+(sqlite3_stmt *)getMessages:(NSString *)SQL
{
sqlite3_stmt *dataset=nil;
sqlite3_stmt *finaldataset=nil;
if (sqlite3_open([[self getdatabasePath] UTF8String], &database) == SQLITE_OK ) {
if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &dataset, NULL) != SQLITE_OK)
{
NSLog(#"error get message");
return nil;
}else{
finaldataset= dataset;
}
}
sqlite3_close(database);
return finaldataset;
}

GoTo to a label in another function

i need to do like this:
-(void)function1{
goto start;
}
-(void)function2{
//some code
start://i need to get in here exactly, [self function2] oblige me to execute the function2 from the beginning
//some code..
}
Seems i can not, what can i do instead? thanx in advance.
EDIT: here is my actual code:
-(void)viewWillAppear:(BOOL)animated{
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"Select quiz,question,p1,p2,p3,num_correct from question where quiz=(select id from quiz where nom = \'%#\' and niveau= \'%i\' and theme=(select id from theme where nom=\'%#\'))",nomPartieQuiz,quiIslam.theGameLevel,quiIslam.themeChoisie];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(contactDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
start://étiquette
while (sqlite3_step(statement) == SQLITE_ROW && !passToNextQuestion)
{
NSString *addressField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 1)];
NSLog(#"%#",addressField);
txtView.text=addressField;
passToNextQuestion=NO;
}
sqlite3_finalize(statement);
}
sqlite3_close(contactDB);
}
}
-(IBAction)clickOnTheButton:(id)sender{
btn1.hidden=YES;
goto start;
}
Try this:
-(void)function1{
[self function2];
}
-(void)function2{
//some code..
}
And, using "gotos" its only allowed in a very very very specifically kind of situations, you should forget about them right now! It goes again any developing principle.
In regards to your edit, put the logic for the start: into it's own method, then call that method from your viewWillAppear and from any other place you need to execute it. Keep in mind you may need to make some variable global so your method can see them, but this is the idea, even if you have to modify the execution.
-(void)sqlStartLogic {
while (sqlite3_step(statement) == SQLITE_ROW && !passToNextQuestion)
{
NSString *addressField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(statement, 1)];
NSLog(#"%#",addressField);
txtView.text=addressField;
passToNextQuestion=NO;
}
sqlite3_finalize(statement);
}
-(void)function2 {
[self sqlStartLogic];
}

IOS sqlite update query issues

I am very new to IOS programming so I am not quite sure how this issue can be best solved.
I have a database of events that are being displayed on a tableview, when you select one of those items, it shows details along with the option to save it as a "favorite" which just updates a column in the db and sets it to 1.
I have a second table view that looks for all instances that have their "favorite" set to 1.
The problem I have run into is that I have only figured out how to get the "updateItem" database query to function off of the path of the item that you selected. Since each tableview shows the same item on different paths, the "updateItem" query is updating the incorrect item in the database when you are using the "favorites" table view.
I understand that in the UpdateItemAtID method, it is using the aryDatabase, and the favorites query uses the aryDatabaseFav. I had to create the second array the get around the favorites tableview, maybe there is a better way to have everything I want with just the original aryDatabase array rather than having 2 different arrays.
Here is the code I have for the two select statements as well as the update query, is there a better way that I can get around needing the path of the item?
-(void)readItems {
if (!database) return; // earlier problems
if (!selStmt)
{
const char *sql = "SELECT * FROM Events;";
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
[aryDatabase 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
//bindings omitted
[aryDatabase addObject:se]; // add to list
[se release]; // free item
}
sqlite3_reset(selStmt); // reset (unbind) statement
selStmt = nil;}
-(void)readFavItems {
if (!database) return; // earlier problems
if (!selStmt)
{
const char *sql = "SELECT * FROM Events WHERE mySchedule = 1;";
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
[aryDatabaseFav 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
//bindings omitted
[aryDatabaseFav addObject:se]; // add to list
[se release]; // free item
}
sqlite3_reset(selStmt); // reset (unbind) statement
selStmt = nil;}
-(void)updateItemAtID:(NSIndexPath *)path {
singleEvent *i = (singleEvent *)[aryDatabase objectAtIndex:path.row];
int ret;
const char *sql = "UPDATE events SET mySchedule = ? WHERE _id = ?;";
if (!updStmt)
{ // build update statement
if ((ret=sqlite3_prepare_v2(database, sql, -1, &updStmt, NULL))!=SQLITE_OK)
{
NSAssert1(0, #"Error building statement to update items [%s]", sqlite3_errmsg(database));
}
}
// bind values to statement
NSInteger m = i.intId;
sqlite3_bind_int(updStmt, 1, m);
NSInteger p = i.intPk;
sqlite3_bind_int(updStmt, 2, p);
if ((ret=sqlite3_step(updStmt)) != SQLITE_DONE)
{
NSAssert1(0, #"Error updating values [%s]", sqlite3_errmsg(database));
}
sqlite3_reset(updStmt);
}
I've updated the code and getting a result=0 on sqlite3_bind_text. Does mean it was updated with no error or no record was updated at all? Not sure what else to do at this point...
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"bh.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
NSLog(writableDBPath);
if(success)
{
const char *dbPath= [writableDBPath UTF8String];
//[G msgBox:writableDBPath title:#""];
//const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
sqlite3 *contactDB = NULL;
if (sqlite3_open(dbPath, &contactDB) == SQLITE_OK)
{
NSString *updateSQL = #"UPDATE account SET name = ? ";
const char *update_stmt = [updateSQL UTF8String];
int e = sqlite3_prepare_v2(contactDB, update_stmt, -1, &statement, nil);
if(e != SQLITE_OK) {
NSLog(#"Problem with updateEntryWithUniqueID");
NSLog(#"Error Code: %d, message '%s'", e, sqlite3_errmsg(contactDB));
return;
}else{
NSLog(#"Updated");
[G setNickname:thisNickname];
}
int result=sqlite3_bind_text(statement, 1, [thisNickname UTF8String], -1, SQLITE_TRANSIENT);
NSLog(#"bind result= %i", result);
if(sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"Problems updating");
}
/* Finished */
sqlite3_finalize(statement);
}else
{
NSLog(#"Can not open DB.");
}
}else{
NSLog(#"path not exsist"); }
Base of my understanding, it seems like the way the NSArray is structured when they load the data could be different since you are using NSIndexPath.
I do recommend the following approach, using a Singleton Object.
Singleton Object works like a container that is shared among a fews view controller(s) or classes.
Back to your issue, you can store the object (singleEvent) in the Singleton for the other uitableview to pick it up.
This is assuming that your singleEvent object have the key fields such as Id.
Hope this helps.