I have some code that adds tickets to a database. I am able to add just fine, but when I try updating existing tickets in the database I can only update once and then every update after that fails. If I restart the app, I can again update once and every update attempt after that fails. Can someone please help me find whats causing this. Here is my code that adds/updates to the database.
sqlite3_stmt *statement;
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_ticketDB) == SQLITE_OK)
{
//first I check to see if ticket already exists
NSString *insertSQL = [NSString stringWithFormat:#"SELECT * FROM tickets WHERE ticketnum = \'%#\'", globalData.ticket];
int rc = sqlite3_prepare_v2(_ticketDB, [insertSQL UTF8String], -1, &statement, nil);
if (rc == SQLITE_OK) {
//Based on if ticket exists, I either update or insert new row
if (sqlite3_step(statement) == SQLITE_ROW) {
insertSQL = [NSString stringWithFormat:#"UPDATE tickets SET shippername = \'%#\' WHERE ticketnum = \'%#\'", globalData.shipperName, globalData.ticket];
}
else{
insertSQL = [NSString stringWithFormat:#"INSERT INTO TICKETS (ticketnum, shippername) VALUES (\"%#\", \"%#\")", globalData.ticket, globalData.shipperName];
}
}
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(_ticketDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Added/Updated");
} else {
NSLog(#"Failed to add/update ticket to db");
}
sqlite3_finalize(statement);
sqlite3_close(_ticketDB);
}
[from Googie's comment:]
You never finalize your "SELECT" statements, instead you prepare another statement using the very same variable to store it. The old statement doesn't get finalized automatically, it just gets lost in memory and probably that's what's holding "UPDATE" statement from updating such row, because the row is locked by the "SELECT" statement.
Just add one more
sqlite3_finalize(statement);
before
sqlite3_prepare_v2(_ticketDB, insert_stmt, -1, &statement, NULL);
Related
I have some values in labels and i'm trying to store it in my database but when i hit save button it isn't save and log a message failed to save. The database is properly made but data is not storing. My code for saving data is this,
- (IBAction)btnSave:(id)sender {
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO data (ID, ALERT, XREF,TEXT,GRAPHIC,PROMPT,VOICE) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\",\"%#\",\"%#\")", _lblID.text, _lblAlert.text, _lblxref.text,_lbltext.text, _lblgraphic.text, _lblprompt.text,_lblvoice.text];
NSLog(#"DDD %#",insertSQL);
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
_lblstatus.text = #"Contact added";
} else {
_lblstatus.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
}
if (sqlite3_step(statement) == SQLITE_DONE){
_lblstatus.text = #"Contact added";
} else {
NSLog(#"prepare failed: %s", sqlite3_errmsg(contactDB));
_lblstatus.text = #"Failed to add contact";
}
1. Replace this snippet in your code to print error message. It will help you to find exact issue.
2. May be the SQL query which your forming is syntactically wrong.
I am trying to insert a member in sqlite DB member table. After inserting values if I take sqlite3_last_insert_rowid() I can't insert another member. the statement shows SQLITE_BUSY.Here is my code. Please anybody help.
-(NSInteger) saveMember:(TMMember *)member {
const char *dbPath = [databasePath UTF8String];
if (sqlite3_open(dbPath, &database) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:#"insert into members (memberName, memberAmount,shareFlag) values(\"%#\", \"%f\",%d)",member.memberName,member.amount,[[NSNumber numberWithBool:member.shareFlag]intValue]];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt,-1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE)
{
NSInteger lastRowId = sqlite3_last_insert_rowid(database);
member.memberId = lastRowId;
NSLog(#"inserted member id = %ld",lastRowId);
NSLog(#"member is added");
}
sqlite3_finalize(statement);
statement = nil;
}
sqlite3_reset(statement);
sqlite3_close(database);
return 0;
}
This error is getting when sqlite already processing another statement, and you are trying to execute another one. So the db is locked until you finalise the statement.
For more info. Read: SQLite Exception: SQLite Busy
I know this is very frequently asked question and i tried everything that i found here. But problem is still there.
Now the problem is i am trying to delete all records from 3 tables with following code but they are not being deleted
const char *dbpath = [dbPathString UTF8String];
sqlite3 *sqliteDB;
if (sqlite3_open(dbpath, &sqliteDB) == SQLITE_OK)
{
//Delete all records from attempts
sqlite3_stmt *statement;
NSString *deleteSQL = [NSString stringWithFormat: #"DELETE FROM table1"];
const char *delete_stmt = [deleteSQL UTF8String];
sqlite3_prepare_v2(sqliteDB, delete_stmt, -1, &statement, NULL);
sqlite3_finalize(statement);
sqlite3_close(sqliteDB);
}
if (sqlite3_open(dbpath, &sqliteDB) == SQLITE_OK)
{
//Delete all records from attempts
sqlite3_stmt *statement;
NSString *deleteSQL = [NSString stringWithFormat: #"DELETE FROM table2"];
const char *delete_stmt = [deleteSQL UTF8String];
sqlite3_prepare_v2(sqliteDB, delete_stmt, -1, &statement, NULL);
sqlite3_finalize(statement);
sqlite3_close(sqliteDB);
}
if (sqlite3_open(dbpath, &sqliteDB) == SQLITE_OK)
{
//Delete all records from attempts
sqlite3_stmt *statement;
NSString *deleteSQL = [NSString stringWithFormat: #"DELETE FROM table"];
const char *delete_stmt = [deleteSQL UTF8String];
sqlite3_prepare_v2(sqliteDB, delete_stmt, -1, &statement, NULL);
sqlite3_finalize(statement);
sqlite3_close(sqliteDB);
}
I have tried with if conditions like
if (sqlite3_prepare_v2(sqliteDB, delete_stmt, -1, &statement, NULL) == SQLITE_OK) {
NSLog("All records deleted");
}
And this if condition get executed and i do have statement in console log.
I am using Xcode 5.0.
Please help me. Thanx in advance
sqlite3_prepare_v2 just prepares the statement.
To actually execute it, you must also call sqlite3_step.
Dear friend this is little bit complex coding try to use external class for easy coding
this link will help you
How to insert update delete in database operation and also save database in document directory
for delete record you can use
NSString *strDelete = [Database executeScalarQuery:#"DELETE FROM table1"];
Happy Coding!!!
In the code below, the commented-out code works.
But using the saveData method of the DBMgr Class results in "Failded to add contact".
I want to see "Contact added" instead.
-(void) saveData{
NSString *insSQL = [NSString stringWithFormat:#"INSERT INTO CONTACTS (name,address,phone) VALUES (\"%#\",\"%#\",\"%#\")",name.text,address.text,phone.text];
DBMgr *dbmgr = [DBMgr alloc];
if([dbmgr saveData:insSQL]== 0){
status.text = #"Contact added";
}else if([dbmgr saveData:insSQL]== 1){
status.text=#"Failded to add contact";
}
/*sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if(sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat:#"INSERT INTO CONTACTS (name,address,phone) VALUES (\"%#\",\"%#\",\"%#\")",name.text,address.text,phone.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE)
{
status.text = #"Contact added";
name.text = #"";
address.text = #"";
phone.text = #"";
}else{
status.text=#"Failded to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}*/
}
-(NSInteger) saveData:(NSString *) querySQL{
NSInteger result;
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if(sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = querySQL;
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE)
{
result = 0;
}else{
result = 1;
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
}
return result;
}
You should check the result codes of all of your SQLite calls, and if they fail, log the error:
- (NSInteger) saveData:(NSString *) querySQL{
NSInteger result = 1;
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if(sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
NSString *insertSQL = querySQL;
const char *insert_stmt = [insertSQL UTF8String];
if (sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL) != SQLITE_OK)
NSLog(#"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(contactDB));
else
{
if(sqlite3_step(statement) == SQLITE_DONE)
{
result = 0;
}else{
NSLog(#"%s: step failed: %s", __FUNCTION__, sqlite3_errmsg(contactDB));
}
sqlite3_finalize(statement);
}
sqlite3_close(contactDB);
} else {
NSLog(#"%s: open failed", __FUNCTION__);
}
return result;
}
Unless you look at sqlite3_errmsg, you're just guessing. And check sqlite3_prepare_v2 return code, too, like I did above, (as that will more likely be the initial indication of a problem).
Two other, unrelated, observations:
The DBMgr should be initialized, e.g.:
DBMgr *dbmgr = [[DBMgr alloc] init];
You are building your INSERT statement using stringWithFormat. That's very dangerous, you should use ? placeholders in your SQL:
const char *insSQL = "INSERT INTO CONTACTS (name,address,phone) VALUES (?, ?, ?)";
sqlite3_prepare_v2(contactDB, insSQL, -1, &statement, NULL);
Then, after preparing that statement, you should then use the sqlite3_bind_text function to assign your values to those three placeholders, e.g.
sqlite3_bind_text(statement, 1, [name.text UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [address.text UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 3, [phone.text UTF8String], -1, SQLITE_TRANSIENT);
By the way, if you wanted to specify NULL, you'd call sqlite3_bind_null instead of sqlite3_bind_text.
Obviously, check the return code from each of those to make sure you returned SQLITE_OK for each, again, logging sqlite3_errmsg if it failed.
I appreciate that this change is going to require some refactoring of your code, but it's important to use sqlite3_bind_text to avoid SQL injection attacks and errors that will result if the user typed in a value that included quotation marks.
By the way, if you're looking at the above and realizing that it takes a lot of code to do this properly, you might want to consider using FMDB which can significantly simplify your life.
i have a view Controller called as VegQuantity which does totalcost=(quantity*cost of the dish) and inserts the itemname,quantity,totalcost into a table called as FINALORDER with database name FinalOrder
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (\"%#\", \"%#\", \"%#\")", itemName.text, input.text, output.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(FinalOrder, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"Couldnt add");
}
sqlite3_finalize(statement);
sqlite3_close(FinalOrder);
}
Final View Controller viewdidload method
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &FinalOrder) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM FINALORDER"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(FinalOrder, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
NSString *qua = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
quantity.text = qua;
NSString *total = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
totalcost.text=total;
}
sqlite3_finalize(statement);
}
sqlite3_close(FinalOrder);
}
But i keep getting this error called expected expression before FinalOrder,and is it correct for me to write this code inside viewdidload? i dont have any button in Final view controller i have a button called order in a view controller called as Restaurant which actually shows me Final view controller..am i supposed to search for the db file again in the Final viewcontroller and i am sorry question seems kind of vague but in brief i just want to know how to retrieve and display the data which i have inserted in VegQuantity view controller into the final view controller thanks
I believe that the problem must rest in the definition of FinalOrder which, on the basis of the error message, looks like has been defined as a class, not as a sqlite3 * variable. Given that the scope of your usage of the database is limited to these two methods, I'd suggest defining a sqlite3 * within that scope, and use that, such as:
- (void)saveRecord
{
sqlite3 *database;
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
[self purgeTable:database];
NSString *insertSQL = #"INSERT INTO FINALORDER (itemname, quantity, totalcost) VALUES (?, ?, ?)";
if (sqlite3_prepare_v2(database, [insertSQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
sqlite3_bind_text(statement, 1, [itemName.text UTF8String], -1, NULL);
sqlite3_bind_int(statement, 2, [input.text intValue]);
sqlite3_bind_double(statement, 3, [output.text doubleValue]);
if (sqlite3_step(statement) == SQLITE_DONE)
{
// status.text = #"Contact added";
// name.text = #"";
// address.text = #"";
// phone.text = #"";
NSLog(#"added");
} else {
NSLog(#"%s Couldn't add; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note, in addition to using a sqlite3 * variable for the database, to make this a little more robust:
I have replaced the stringWithFormat statement that built the SQL insert statement with an INSERT statement that uses the ? placeholders and then use sqlite3_bind_text to bind values to that statement. This way, if someone entered a value that included a quotation mark, the insert statement will still work (your original implementation would have crashed and/or was susceptible to a SQL injection attack).
I have also added the sqlite3_errmsg statements so if something goes wrong, I know what the problem was.
Rather than treating these three fields as text fields, I'm assuming your table is defined as CREATE TABLE IF NOT EXISTS FINALORDER (itemname TEXT, quantity INT, totalcost REAL); and therefore use text, int, and double bind statements.
This, incidentally, invokes a purgeTable method, so if you run it twice, it will remove the old record in there:
- (void)purgeTable:(sqlite3 *)database
{
if (sqlite3_exec(database, "DELETE FROM FINALORDER;", NULL, NULL, NULL) != SQLITE_OK)
NSLog(#"%s Couldn't purge table %s", __FUNCTION__, sqlite3_errmsg(database));
}
Anyway, you can then read this data via:
- (void)loadRecord
{
sqlite3 *database;
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString *querySQL = #"SELECT * FROM FINALORDER";
if (sqlite3_prepare_v2(database, [querySQL UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *itemname = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
item.text = itemname;
//[itemname release]; // if not ARC, uncomment this line
int qua = sqlite3_column_int(statement, 1);
quantity.text = [NSString stringWithFormat:#"%1d", qua];
double total = sqlite3_column_double(statement, 2);
totalcost.text = [NSString stringWithFormat:#"%1.2f", total];
}
sqlite3_finalize(statement);
} else {
NSLog(#"%s Couldn't prepare; errmsg='%s'", __FUNCTION__, sqlite3_errmsg(database));
}
sqlite3_close(database);
}
}
Note,
I've added a sqlite3_errmsg log statement if the sqlite3_prepare fails, and I've retired the stringWithFormat (because you weren't formatting anything).
Given that quantity was INT and total was REAL, I'm retrieving the values using the appropriate variation of sqlite3_column_text, sqlite3_column_int, or sqlite3_column_double, as appropriate.
I infer that you're not using ARC and therefore, the [NSString alloc] must have an associated release or autorelease.
Finally, in our chat, you said that you received an error message (presumably an "Undefined Symbols" message) that said:
OBJC_CLASS_$"_FinalOrder"
This means that you are trying to use an object class of FinalOrder, but you haven't defined that class anywhere. Take a look at which .o files it's reporting this error for and look at the corresponding .m file, and look for your use of the FinalOrder class there. You clearly have a FinalOrder class interface defined somewhere, but never defined the class implementation, or, if you have one, for some reason it's not included in your target's "Compile Sources" listing, so double check it's here:
Finally, by the way, make sure your database is in the Documents folder, not trying to open a copy of the database in your project's bundle (because that's read only). If you have a copy of the database in your bundle, just check to see if you already have it in you Documents folder, and if not, copy it from the bundle to the Documents folder.