iOS - SQLite not updating but prepare and step are successfully executing - objective-c

The title is pretty much all the information I have because I cannot find an issue with the code. The same instance of database that is being used in the code below is able to successfully insert, delete and select, but this function does not work. No error code is retured(Always 0), and the program continues to run nominally after executing this function.
Here is the table information:
reminder (uniqueID integer primary key autoincrement, title text not null, details text, time integer)
Code in question:
- (void) updateEntryData:(ReminderData *)data
{
sqlite3_stmt *statement;
char *updateCommand = "UPDATE reminder SET title = '?', details = '?', time = ? WHERE uniqueID = ?";
int e = sqlite3_prepare_v2(database, updateCommand, -1, &statement, nil);
if(e != SQLITE_OK) {
NSLog(#"Problem with updateEntryWithUniqueID");
NSLog(#"Error Code: %d, message '%s'", e, sqlite3_errmsg(database));
return;
}
sqlite3_bind_text(statement, 1, [data.title UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [data.details UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 3, [data.time timeIntervalSince1970]);
sqlite3_bind_int(statement, 4, data.uniqueID);
NSLog(#"\nID: %d\nTitle: %#\nDetails: %#\nTime: %#", data.uniqueID, data.title, data.details, data.time);
if(sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"Problems updating entry in reminder");
}
/* Finished */
sqlite3_finalize(statement);
}
Any help would be greatly appreciated; I am stumped.
Edit: Forgot to mention that the sql command used works when entered into the sqlite console.

Just remove the single quotes around the parameters. Wrapping the placeholders will make it a value and not a parameter anymore.
char *updateCommand = "UPDATE reminder SET title = ?, details = ? .....";

Related

How to bind a LIKE wildcard in SQLite3

I'd like to run the SQL query: SELECT COUNT(id) AS nbr FROM user WHERE name LIKE '%John%', and get the number of records matched by the keyword John.
So I've written the following code :
unsigned int nbr;
char *sql, *like;
sqlite3 *db;
sqlite3_stmt *stmt;
/* Database connection etc. */
like = _make_string("%%%s%%", keyword); /* keyword: John */
printf("Like clause: %s\n", like);
sql = _make_string("SELECT COUNT(id) AS nbr FROM user WHERE name LIKE ?");
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, like, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) {
/* error, SQL execution failed */
}
nbr = sqlite3_column_int64(stmt, 1);
printf("Number of record: %u\n", nbr);
/* Free strings, finalize statement, disconnect db etc. */
For reason of simplicity, some return value tests are ignored in the snippet. _make_string(const char *fmt, ...) is a printf-like function which can form strings, and it did return %John% to me.
However, my code always returns 0 record. I have tried the SQL in sqlite3 command line shell, but it has found 2 records.
Any idea please ?
I've hard coded the LIKE clause and removed the sqlite3_bind_text function, and it doesn't change the result.
sql = _make_string("SELECT COUNT(id) AS nbr FROM client WHERE name LIKE '%John%'");
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (sqlite3_step(stmt) != SQLITE_ROW) {
/* error */
}
nbr = sqlite3_column_int64(stmt, 1);
printf("Number of record: %u\n", nbr);
The program always returns 0 records.
Well, I've found the answer in the mailing list archive of SQLite Why bind indexes start from 1 and column indexes start from 0?
In fact, the bind index starts from 1, but the column index starts from 0. So the code should be modified as the following
nbr = sqlite3_column_int64(stmt, 0);
printf("Number of record: %u.\n", nbr);
Now the program returns 2 (records) !

Print / Log the SQLite Statement after the binding

I like to log the compiled Statement after this:
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_int( compiledStatement, 1, updateThis.web_id);
[...]
}
NSLog(#"Put out the complete SQLite Statement.");
The direct output failed, and i think this is not the way to do it:
NSLog(#"%#",compiledStatement);
You can't print the compiledStatement. What you can do is implement the sqlite3_trace callback function. This will print every executed sql statement.
To implement the function add this before your #implementation block
void sqliteCallbackFunc(void *foo, const char* statement) {
NSLog(#"=> %s", statement);
}
This is the function you will point to.
To point to this function simple call:
sqlite3_trace(db, sqliteCallbackFunc, NULL);

How do I rollback a SQLite DB when iPhone application quits while or during inserting data into table?

I am using SQLite (without any external library) for my iPhone app. I have huge data of nearly 500 rows (coming from web services so it cannot be inserted manually from sqlite manager) to be inserted into a SQLite table.
I checked the logs and insert is executed successfully.
if ( sqlite3_exec(database, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
But when the app is quit in the middle of insert operation, only some rows get inserted, for example say 200. Thus is there a way to do a complete rollback when the application quits in the midde of insert operation?
Thus when I restart the application, I am reading only 200 rows.
Note: I am using NSOperationQueue and thus my app runs without any glitch during insertion (which takes nearly 20 secs for whole insert)
Is there a reference tutorial for this case to avoid incomplete insertion into SQLite?
I know that I may have to use transaction for this but is there any good ref?
Any help would be appreciated.
You can just start transaction and end. Simple case:
sqlite3_exec(database, "BEGIN", 0, 0, 0);
... (your insert code)
sqlite3_exec(database, "COMMIT", 0, 0, 0);
Thankfully i found the way out:
if ([db_handle openConnectionSucess]==1 && (sqlite3_exec(database, "BEGIN", 0, 0, 0))==SQLITE_OK) {
char *error;
NSString *insertStatement = [NSString stringWithFormat:#"insert into StationTbl (id, name, address) VALUES (\"%#\", \"%#\", \"%f\", \"%f\")",id,name,value];
if ( sqlite3_exec(database, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
}
else
{
NSLog(#"Error: %s", error);
}
if(sqlite3_exec(database, "COMMIT", 0, 0, 0) ==SQLITE_OK){
}
else {
sqlite3_exec(database, "ROLLBACK", 0, 0, 0);
}
}
It is solving 2 purpose:
the insert is executed in 3 secs max (as comp to 25 secs without
using transactions
it is auto rollbacked if app is quit .
Thank all who made an effort in the post!!

Delete column from Sqlite database

Basically I have to enter all textfield values in database then have to use for sending to webservice. So when the one column details send to service then have to delete that column.
I have done this:
-(void)deleteTableDataFromSavedDataTable:(NSString *)lastID {
NSString *sql_str=[NSString stringWithFormat:#"DELETE FROM FormInfoValues where Phone = %#",lastID];
const char *sql = [sql_str UTF8String];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"sql delete statement is ");
sqlite3_stmt *deleteStmt;
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
NSLog(#"sql delete statement is %#", deleteStmt);
if(sqlite3_step(deleteStmt) != SQLITE_DONE )
{
NSLog( #"Error: %s", sqlite3_errmsg(database) );
}
else
{
NSLog( #"row id = %lld", (sqlite3_last_insert_rowid(database)+1));
NSLog(#"No Error");
}
}
sqlite3_finalize(deleteStmt);
}
sqlite3_close(database);
}
but its not deleting after sending. Why its not calling?
give me ideas..
It's quite possible I'm not understanding your issue properly -- so I apologize in advance if so.
Regarding: "...then have to delete that column."
-- If you want to clear the data in a column (by setting it to zero, or empty string, or NULL) then you'll want to use the UPDATE command in SQL. The DELETE command always deletes ROWS.
-- If you really must remove a column from a table's schema, you'll have to create a new table in sqlite. (Sqlite allows you to ADD a column via ALTER TABLE command, and some other databases DO allow you to drop a column using ALTER TABLE as well.) You can quickly copy a table (without it's constraints, etc) via e.g.:
CREATE TABLE MyOutput as SELECT a,b,d,f,h,z from MyOriginal;
-- If your output is created by a SELECT statement, just avoid using "*" and specify just the columns you want included.

sqlite3 datetime not functioning properly on iPhone

I am having trouble getting sqlite3 to do comparisons between datetimes. On my mac, when I run the sql queries, it works. However, on the iPhone, the exact same query fails.
I have a sqlite3 table setup as follows:
CREATE TABLE IF NOT EXISTS `Artists` (
`id` int(11) NOT NULL PRIMARY KEY,
`name` varchar(256) NOT NULL,
`lastUpdate` date NOT NULL default CURRENT_TIMESTAMP ,
...
...
);
I can insert an artist with:
INSERT OR IGNORE INTO `Artists` (`id`,`name`) VALUES ('1','Justin');
I am trying to find the number of artists that have not been updated for the last 2 seconds (to keep it short):
SELECT COUNT(*) FROM `Artists` WHERE `id` = ? AND `lastUpdate` < datetime('now','-2 seconds');
On the iPhone, this returns 0. On my pc, it returns 1. I am expecting the value to be 1. What is the difference? I am retrieving the value by:
-(BOOL) artistNeedsUpdating:(int)artistId
{
[dbLock lock];
BOOL result = NO;
sqlite3_stmt *statement;
const char* query = "SELECT COUNT(*) FROM `Artists` WHERE `id` = ? AND `lastUpdate` < datetime('now','-2 seconds');";
if(sqlite3_prepare_v2(database, query, -1, &statement, NULL) == SQLITE_OK){
sqlite3_bind_int(statement, 1, artistId);
if(sqlite3_step(statement) == SQLITE_ROW){
if(sqlite3_column_int(statement, 0) > 0) // If 0, then we don't need to update
result = YES;
}
sqlite3_finalize(statement);
}
[dbLock unlock];
return result;
}
I am confused as to why it works on one platform but not the other, does anybody have any ideas?
Thank you,
Justin
Nevermind, it was my mistake.
I was updating the row after it got done with
UPDATE `Artists` SET `lastUpdate`='CURRENT_TIMESTAMP';
When it should have been:
UPDATE `Artists` SET `lastUpdate`=CURRENT_TIMESTAMP;