sqlite3 update not working in ios - iphone

I am trying to update sqlite db. This is the code I am using
if(sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
const char * sql;
sql = "update tms set name = ?,place=?,stars=? where id=?";
sqlite3_stmt *selectStatement;
//prepare the select statement
int returnValue = sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
sqlite3_bind_text(selectStatement, 1,[[payloadDict valueForKey:#"userName"] UTF8String] , [[payloadDict valueForKey:#"userName"] length],SQLITE_STATIC);
sqlite3_bind_text(selectStatement, 2,[[payloadDict valueForKey:#"locName"] UTF8String], [[payloadDict valueForKey:#"locName"] length],SQLITE_STATIC);
sqlite3_bind_int(selectStatement, 3, [[payloadDict valueForKey:#"starCount"] integerValue]);
sqlite3_bind_int(selectStatement, 4, [[payloadDict valueForKey:#"rowid"] integerValue]);
int success = sqlite3_step(selectStatement);
if(success == SQLITE_DONE)
{
isExist = TRUE;
}
else {
//NSAssert1(0,#"Error: Failed to Update %s",sqlite3_errmsg(database));
}
}
I am getting value 101 as success when sqlite3_step is executed. But database is not updated with new values.
How can I do this properly?
Thanks

I agree with #ott's excellent suggestion of making sure the database is located in the Documents directory (though I would have thought that that would have given you an error).
I'd also double check the value returned by [[payloadDict valueForKey:#"rowid"] integerValue] to make sure it matches a value in the id column for one of the existing rows in your table. If it doesn't match anything, sqlite3_step will return SQLITE_DONE even if nothing was updated.
Also note that you might also want to make sure that the id values are stored as numeric values, not text strings as sqlite is pretty lax about letting you store values in whatever data type you originally specified when you first inserted the data, regardless of how the table was defined), and I'm not sure if a WHERE clause looking for a numeric match will succeed if the data was originally stored as a text value. If you used an id column definition like id INTEGER PRIMARY KEY AUTOINCREMENT, where the system defined the values automatically for you, this isn't an issue, but if you manually populated the id column, it might be something to double check. (Generally it does a pretty good job in interpreting strings as numbers on the fly, but there are some weird situations that are problematic: For example, if you stored a string value of "5,127" in a numeric field, if you later then try to retrieve its numeric value, sqlite won't know what to do with the comma in the text value "5,127" and will interpret the numeric value as 5, not as 5127.)

Related

sqlite3_bind_parameter_index returns 0?

The count returns a count of the parameters and is good. However the index is returning 0.
Any ideas?
sqlite3 *database;
sqlite3_stmt *updateStmt;
int ID;
const char *sql;
sql = "update User set Name = ? , Dev = ?,ActiveLevel = ? Where _id = ?";
if(sqlite3_prepare_v2(database, sql, -1, &updateStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(database));
NSLog(#"count %d",sqlite3_bind_parameter_count(updateStmt));
NSLog(#"Index %d",sqlite3_bind_parameter_index(updateStmt,"ActiveLevel"));
From the fine manual:
int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
Return the index of an SQL parameter given its name.
And for parameters:
?
A question mark that is not followed by a number creates a parameter with a number one greater than the largest parameter number already assigned.
...
:AAAA
A colon followed by an identifier name holds a spot for a named parameter with the name :AAAA.
Emphasis mine in the second section.
Your SQL doesn't have any named parameters at all, you just have plain old placeholders. The parameter name is the name of the placeholder (:AAAA), not the name of the column in question; remember that you can use placeholders in places where no name could be automatically derived so you have to name them yourself.
If you want to use ActiveLevel as a named parameter, then your SQL should look like this:
update User set Name = ? , Dev = ?, ActiveLevel = :ActiveLevel Where _id = ?
And you'd probably want to replace the other placeholders (?) with named parameters for consistency.

I want to check whether a primary key exists on sqlite table

I have an sqlite database on my Iphone app. That database has a table named "Students"
and It has 10 rows of data with keys from 1 to 11. But I want to test whether a primary key with value "3" exists on the table by objective c coding.
Run the select query, if it returns no record then the record does not exist.
You need to run a select query on it. The in depth info on how to do that is here.
The basic steps are:
open the database
prepare a select statement
bind host values
evaluate the prepared statement
tidy up
The code looks something like this
// Database already opened
// All error checking omitted because I am lazy
sqlite3_stmt statement;
// Replace columns below with the names of your actual columns
// and key with the name of the primary key column
errorResult = sqlite3_prepare_v2(dbConnection,
"select columns from students where key = ?",
-1,
&statement,
NULL);
// error check and handle any
errorResult = sqlite3_bind_int(statement, 1, 3); // Put 3 in place of first question mark
// error check and handle any
while ((errorResult = sqlite3_step(statement) == SQLITE_ROW)
{
// Use sqlite3 column functions to get data
// http://www.sqlite.org/c3ref/column_blob.html
}
// error check and handle any
errorCheck = sqlite3_finalize(statement);
// error check and handle any

iPhone SQLite Database Reading And Writing

So I am trying to work with SQLite in one of iPhone applications and I am using the sqlite3 library. I am able to access the database and even make a query; in fact the query accesses the exact data but for some reason the string I am getting back is a long integer and not the string I was looking for. Here is the database and code:
Filename: Package.sql
Table Lessons
LessonID VARCHAR(64) Primary Key | LessonName VARCHAR(100) | EntryDate (DATETIME) | Chrono VARCHAR (20)
bfow02nso9xjdo40wksbfkekakoe29ak | Learning The History | 2010-08-05 16:24:35 | 0001
And the iPhone Code
...
-(NSString *)getRow:(NSString *)tablename where:(NSString *)column equals:(NSString *)value {
  const char *query = [[[[[[[#"SELECT * FROM `" stringByAppendingString:tablename] stringByAppendingString:#"` WHERE `"] stringByAppendingString:column] stringByAppendingString:#"` = '"] stringByAppendingString:value] stringByAppendingString:#"';"] cStringUsingEncoding:NSUTF8StringEncoding];
  NSString *result;
  if(sqlite3_open([dbpath UTF8String], &database) == SQLITE_OK) {
    sqlite3_stmt *compiledQuery;
    if(sqlite3_prepare_v2(database, query, -1, &compiledQuery, NULL) == SQLITE_OK) {
      while(sqlite3_step(compiledQuery) == SQLITE_ROW) {
        NSString *str_temp = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledQuery, 2)];
        result = str_temp;
        
      }
      sqlite3_finalize(compiledQuery);
    }
    sqlite3_close(database);
  }
 
  return result;
}
...
When the code executes:
CDatabase *db = [[CDatabase alloc]initWithDatabase:#"Package.sql"];
NSString *result = [db getRow:#"Lessons" where:#"Chrono" equals:#"0001"];
the returned value NSString *result has a value of "1,364,111". Why is it doing that??? It should be "Learning The History"
Are you sure that any of your SQLite calls are successful? You should initialize result to nil so that your function returns nil if any errors are caught.
Three (probably related) issues with your code:
The index to sqlite3_column_text should be zero-based; you're passing 2, which should refer to the third column. You probably mean to pass 1. From the docs:
...the second argument is the index of the column for which information should be returned. The leftmost column of the result set has the index 0.
You really shouldn't use SELECT *. Specify the columns you want!
You should specialize your query by binding values, not by concatenating strings! Your code is rife with the possibility of SQL injections (not to mention incorrect queries).
For example (with no error checking):
const char *query = "SELECT * FROM ? WHERE ?=?";
sqlite3_stmt *compiledQuery;
sqlite3_prepare_v2(database, query, -1, &compiledQuery, NULL);
sqlite3_bind_text(compiledQuery, 1, "Lessons", -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledQuery, 2, "Chrono", -1, SQLITE_TRANSIENT);
sqlite3_bind_text(compiledQuery, 3, "0001", -1, SQLITE_TRANSIENT);
Note that the index here is 1-based (I don't know why they do that). From the docs:
The second argument is the index of the SQL parameter to be set. The leftmost SQL parameter has an index of 1.
Haha whoops I realized that I was just displaying the string as a data format by using the %d string format. when i changed it to %# i got the string format

iPhone Sqlite Performance Problem

Hey guys, here is the low down.
I have one table, consisting of a primary key(col1), text(col2), and text(col3). Basically a map. This table contains about 200k rows. It basically takes me about 1.x seconds to retrieve a single row (this is all I want). I'm basically using select * from table where col2 = 'some value'.
I've tried creating an index for all three columns, each column individually, and col2 and col3, but this really hasn't improved my situation at all.
I'm wondering, is this normal ? I haven't come across any posts of people complaing about slow sqlite performance for big tables, so I'm wondering what I'm doing wrong.
Any help would be greatly appreciated.
I would say, that this is absolutely not typically.
Even when you have a large table, an access via an index should be rather fast.
What could you do: Create only one index on col2 (that is the one and only you need for this select!).
Than use "EXPLAIN SELECT ...." to get the information, what SQLite makes out of it. The result is not easy to read, but with some experience it is possible to see if the index is used. You could also post the result here.
I solved the problem. It was that when I created a new sqlite database file, and added it to the project, xcode didn't properly recompile itself, it was still using the old file. I had to remove the old database from the project, remove the compiled version on the computer, clean the project, then compile it and make sure that it was crashing since the database was missing. Then again remove the compiled files, clean it, and re-add the new sqlite database.
This is why even after I created the index there was no performance improvement whatsoever....
Strange, would this be considered a bug with Xcode ?
I've made this class a singleton (called SQLAdapter), and this it contains two methods in here, one to copy the database if its needed, and the other to exec my sql code:
Here is the sql code method, this was the first time I coded in Obj-C, so just ignore the string append methods, I'm changing this as we speak...
- (NSString *)getMapping:(NSString *)test{
//Our return string
NSString *res = test;
// Setup the database object
sqlite3 *database;
NSString *sqlStmnt;
if (direction) sqlStmnt = #"select * from table where col1 = '";
else sqlStmnt = #"select * from table where col2 = '";
NSString *tStmt = [sqlStmnt stringByAppendingString:test];
NSString *sqlState = [tStmt stringByAppendingString:#"'"];
const char * sqlStatement = [sqlState UTF8String];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
sqlite3_stmt *compiledStatement;
//execute the statement
if (sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: during prepare '%s'.", sqlite3_errmsg(database));
}
//bind our translation into the sql select statment
sqlite3_bind_text( compiledStatement, 1 , [word UTF8String], -1, SQLITE_TRANSIENT);
if(sqlite3_step(compiledStatement) == SQLITE_ROW) { //if execution is successful i.e. we get a match
//lets return the desired language translation
res = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, (direction) ? 2 : 1)];
}
sqlite3_finalize(compiledStatement); //Release the compiled statement from memory
}
sqlite3_close(database); //lets return the translation
return res;
}
Pretty much exactly the same way that the SQLiteBooks project does it if I'm not mistaken...

Best way to reference image through out app?

My application is database driven. Each row contains the main content column I display in a UIWebView. Most of the rows (the content column) have a reference to image1 and some to image2. I convert these to base64 and add the image string into the row. However, if either image changes, it means I have to go back through all the rows and update the base64 string.
I can provide a unique string in the row content such as {image1}. It means I'll have to search through the entire content for that row and replace with the base64 version of the image. These images are also always at the bottom of the row content. Not sure how having to go through all content first before replacing will affect performance. Is there a better way to do this?
I hope I am understanding your question correctly.
If the images are not very large, then it is probably OK to just use the like keyword as in:
sqlite3_stmt *statement = nil;
if(statement == nil)
{
const char *sql = [[NSString stringWithFormat:#"SELECT imageContent FROM imageDatabase WHERE imageContent LIKE '%#%#%#'", #"%", imageValue, #"%"] UTF8String];
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) != SQLITE_OK) {
//NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(db));
return;
}
}
while (sqlite3_step(statement) == SQLITE_ROW) {
// query was successful
// perform some action on the resulting data
}
sqlite3_finalize(statement);
statement = nil;
If you set imageValue = image1, image2, or whatever, that will give you the item you are looking for from the database without having to do string manipulation in code. I am assuming you know SQL, so sorry if this is redundant information, but the above will search your imageDatabase for anything that contains the image1, image2 imageValue. Once you find the row, you can update it, or you can use the WHERE clause with the UPDATE SQL statement, but I find that to be a bit dangerous due to the possibility of inadvertently updating multiple rows without checking the content first to make sure it is what you want.
Also, if you are doing database updates with this, you will find a major performance boost by wrapping your inserts and updates with transactions like:
const char *sql = "BEGIN TRANSACTION;";
char *errMsg;
sqlite3_exec(db, sql, nil, 0, &errMsg);
const char *commit = "COMMIT;";
sqlite3_exec(db, commit, nil, 0, &errMsg);
It prepares and optimizes your query before executing it. I have seen insert and update queries get twice as fast with transactions.
If the database is very large, this will have a significant performance hit, but doing the string manipulation in memory will have a large memory cost. If you use the SQLite LIKE method, the string search is done in a serial fashion on disk and has less of a memory hit.
Once you have found the specific item you can do the regular expression search and replace on just that particular string keeping your code's memory footprint smaller.
Why not have the images in a table, with image_ID (a unique integer) and image_data (a blob)? Then in your main table, store just the image_ID, and do a join if you need the actual image?
On an alternative interpretation of your question (if that answer doesn't mnake sense to you) why not break the content into three fields: stuff before the image, the image, and the stuff after. Store the image_ID for the middle part (not the data--get that with an sql JOIN on the image table). Then build the final content with concatenation.