Sqlite statements for iOS - iphone

I am trying to create a table with a number of columns. I am trying to bind the value (which is a variable) to create the table name. I keep getting an error: ('Error while creating update statement. 'near "?": syntax error'') So obviously I am doing something wrong with trying to bind it. Can anyone shed some light on this for me?
- (void)addTable{
NSString *cat = sourceName;
if(addtablestmt == nil) {
const char *sqlStr = "CREATE Table ? ('itemID' 'integer','itemName' 'char(50)','itemCategory' 'char(50)','itemCount' 'integer','itemDone' 'char(50)','itemNote' 'char(50)','itemOrder' 'char(50)',PRIMARY KEY (itemID))";
if(sqlite3_prepare_v2(database, sqlStr, -1, &addtablestmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(database));
sqlite3_bind_text(addtablestmt, 1, [cat UTF8String], -1, SQLITE_TRANSIENT);
}
if(SQLITE_DONE != sqlite3_step(addtablestmt)){
NSAssert1(0, #"Error while updating. '%s'", sqlite3_errmsg(database));
}
sqlite3_reset(addtablestmt);
}

You might try something more like:
NSString *sqlStr = [NSString stringWithFormat:#"CREATE Table %# ('itemID' 'integer','itemName' 'char(50)','itemCategory' 'char(50)','itemCount' 'integer','itemDone' 'char(50)','itemNote' 'char(50)','itemOrder' 'char(50)',PRIMARY KEY (itemID))", sourceName];
if(sqlite3_prepare_v2(database, [sqlStr UTF8String], -1, &addtablestmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(database));

You cannot bind table name as a parameter. If you are planning on doing that, then you need to dynamically create the string as suggested by Joe. However be careful as that approach could lead to sql injection attacks. For ex a user could enter a name "; Delete from sqlite_master".

Related

SQLite- how to insert data in ios

i tried some sqlite program for practice when i insert the record with this code it give some error
-(void) insertRecordIntoTableNamed:(NSString *) tableName
withField1:(NSString *) field1
field1Value:(NSString *) field1Value
andField2:(NSString *) field2
field2Value:(NSString *) field2Value {
NSString *sql = [NSString stringWithFormat:
#"INSERT INTO ‘%#‘ (‘%#‘, ‘%#‘) VALUES (‘%#‘,’%#‘)",tableName, field1, field2, field1Value, field2Value];
char *err;
if (sqlite3_exec(db, [sql UTF8String], NULL, NULL, &err) != SQLITE_OK) {
sqlite3_close(db);
NSAssert(0, #"Error updating table.");
}
}
the error message is
** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error updating table.'
i try to insert data through terminal and the data is retrived properly
so Please help me for inserting data through programms
may it works better when you use a char * as sql statement as an NSString object. For example:
char *sql = "INSERT INTO ‘?‘ (‘?‘, ‘?‘) VALUES (‘?‘ ....";
to replace the ? with your NSString arguments use the function below.
sqlite3_bind_text(insert_statement, 1, [tableName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insert_statement, 2, [field1 UTF8String], -1, SQLITE_TRANSIENT);
etc....
First you have to define the insert_statement.
static sqlite3_stmt *insert_statement = nil;
... and at the end you should reset the statement with sqlite3_reset(insert_statement);
LEFT SINGLE QUOTATION MARK is unsuitable for quoting either identifiers or values in SQLite. Quote identifiers with " (\x22) and data with ' (\x27).
Also, I hope your program is simple enough that potential SQL injection doesn't matter.

Table still exists after DROP TABLE

My iPhone application has a code like this to delete a table and re-create it.
const char *sql = [#"DROP TABLE mytable" cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
sqlite3_step(statement);
NSLog(#"dropped.");
} else {
NSLog(#"error. %s", sqlite3_errmsg(database));
}
sqlite3_finalize(statement);
// ...
// DO SQL LIKE `CREATE TABLE mytable` here.
In most cases, this code works.
But error reports say, the table still exist after the first SQL (DROP TABLE mutable) ran without an error in some rare cases. And I have never experienced that case on my devices.
Has anyone experienced this? or any information?

iPhone, sqlite3, how do I pass values into a select statement?

This is the code I'm using to select some records from my database. I'm binding two dates into my sql, however, when I get to sqlite3_step I get SQLITE_DONE where I should be getting SQLITE_ROW. It looks like its processing the bindings rather than querying the data.
What am I doing wrong ?
NSString *startDateRangeString = #"2000-05-01";
NSString *endDateRangeString = #"2011-05-01";
sqlite3 *database;
int result = sqlite3_open("mydb.db", &database);
if(result != SQLITE_OK)
{
NSLog(#"Could not open db.");
}
const char *sql = "select pid from tmp where due >= '%#' and due < '%#' order by due, pid;";
sqlite3_stmt *statementTMP;
int error_code = sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if(error_code == SQLITE_OK) {
sqlite3_bind_text(statementTMP, 1, [startDateRangeString UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statementTMP, 2, [endDateRangeString UTF8String], -1, SQLITE_TRANSIENT);
int step_error_code = sqlite3_step(statementTMP);
while(sqlite3_step(statementTMP) == SQLITE_ROW) // I get 101 aka SQLITE_DONE
{
NSLog(#"Found!!");
}
}
sqlite3_finalize(statementTMP);
sqlite3_close(database);
I think your SQL is bad. Instead of %# you should use ? for the positional parameters.
I strongly suggest using a wrapper to simplify life. FMDB is a great one at http://github.com/ccgus/fmdb.
char *statementTMP = "select pid from tmp where due >= '?1' and due < '?2' order by due, pid";
....
sqlite3_bind_text(statementTMP, 1, [startDateRangeString UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statementTMP, 2, [endDateRangeString UTF8String], -1, SQLITE_TRANSIENT);
This is by design.
sqlite3_step returns SQLITE_ROW for each row in the resultset, and SQLITE_DONE to indicate there are no more rows. So if a resultset contains N rows, then N calls would return SQLITE_ROW and (N+1)st returns SQLITE_DONE. An empty resultset is not in any way special, it follows the same logic with N=0 (so the very first call returns SQLITE_DONE). This allows client code to handle all resultsets uniformly.
Hope that helps.
Let me know if you need anymore help.
PK
If I read correctly.. You are not executing the statement.. you are just preparing.. you have to do both..
this part just prepare the query
if (sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) != SQLITE_OK)
{
NSLog(#"SQL Warning: failed to prepare statement
with message '%s'.", sqlite3_errmsg(database));
}
This part actually executes the query
if(sqlite3_exec(database, sql, nil, &stmt, &errmsg) == SQLITE_OK)
{
NSLog(#"it means that you query executed correctly");
if(sqlite3_step(stmt) == SQLITE_ROW)
{
NSLog(#"Found!!");
}
}else
{
NSLog(#"SQL Warning: '%s'.", sqlite3_errmsg(database));
}
=)

sqlite3 runs INSERT and UPDATE queries fine first time, but second time returns error

the following code (from a database object created for each new view):
-(void)update:(NSString *)queryText{
NSLog(queryText);
char *errorMsg;
if (sqlite3_prepare_v2(database, [queryText UTF8String], -1, &statement, nil) == SQLITE_OK) {
} else {
NSLog(#"HMM, COULDNT RUN QUERY: %s\n", sqlite3_errmsg(database));
}
if (sqlite3_step(statement) != SQLITE_DONE){
NSAssert1(0, #"Error updating table: %s", errorMsg);
}
sqlite3_finalize(statement);
NSLog(#"Update saved");
}
-(void)insert_answer:(int)obj_id question_type:(NSString *)question_type score:(int)score{
char *errorMsg;
char *update = "INSERT INTO Answers (obj_id, question_type, score, date) VALUES (?, ?, ?, DATE())";
//sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, obj_id);
sqlite3_bind_text(statement, 2, [question_type UTF8String], -1, NULL);
sqlite3_bind_int(statement, 3, score);
}
if (sqlite3_step(statement) != SQLITE_DONE){
NSAssert1(0, #"Error inserting: %s", errorMsg);
}
sqlite3_finalize(statement);
NSLog(#"Answer saved");
}
is called by my app from this code:
[db insert_answer:[[dict_question objectForKey:#"JISDec"] intValue] question_type:#"kanji_meaning" score:arc4random() % 100];
//
//update EF, Next_question, n here
//
[db update:[NSString stringWithFormat:#"UPDATE Kanji SET EF = %d WHERE Kanji = '%#'", [[dict_question objectForKey:#"EF"] intValue] + 2, [dict_question objectForKey:#"question"]]];
[db update:[NSString stringWithFormat:#"UPDATE Kanji SET Next_Question = %d WHERE Kanji = '%#'", 1, [dict_question objectForKey:#"question"]]];
[db update:[NSString stringWithFormat:#"UPDATE Kanji SET n = %d WHERE Kanji = '%#'", [[dict_question objectForKey:#"n"] intValue] + 1, [dict_question objectForKey:#"question"]]];
and runs fine the first time a question is answered in my app, but the next time (after a new view is loaded) it returns an assert error and crashes.
Does anyone know why??? I have almost exactly the same code in another app and it has no problems.
Many thanks.
EDIT: Added stack:
2010-03-08 03:01:12.107 Kanji[33864:207] *** Assertion failure in -[databaseConnection update:], /Volumes/Xcode/Kanji/Classes/../databaseConnection.m:58
2010-03-08 03:01:12.108 Kanji[33864:207] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error updating table: (null)'
2010-03-08 03:01:12.109 Kanji Training with Watanabe Sensei[33864:207] Stack: (
30135387,
2561586441,
30219323,
814852,
37730,
18074,
2757637,
3165006,
3173743,
3168955,
2862559,
2770888,
2797665,
37424473,
29920128,
29916232,
37418517,
37418714,
2801583
)
It would help if you would show the 'assert error' and maybe a stacktrace to see what call actually fails.
Also, note that even though you are checking for errors, you are not doing it correctly. Even if an error happens, you continue to exeute sqlite functions. That is a recipe for disaster.

SQLite database - cannot see updates

I have a sqlite database and I am adding to it new words. The problem is that I can see them added to a table only after restarting application. The "SELECT" statement doesn't "see" newly added elements before restarting application.
Why may this happen?
I am creating some kind of a dictionary. Here is how I add new items:
const char *sql_query = "INSERT INTO words(word) VALUES(?)";
if(sqlite3_prepare_v2(database, sql_query, -1, &addWordsStmt, NULL) != SQLITE_OK)
{
return FALSE;
}
sqlite3_bind_text(addWordsStmt, 1, [ word UTF8String], -1, SQLITE_TRANSIENT);
if( sqlite3_step(addWordsStmt) != SQLITE_DONE)
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
sqlite3_reset(addWordsStmt);
sqlite3_finalize(addWordsStmt);
And here is my retrieval code:
const char *sql_query = "SELECT word FROM words WHERE id=?";
if(sqlite3_prepare_v2(database, sql_query, -1, &getWordsStmt, NULL) != SQLITE_OK)
{
return;
}
sqlite3_bind_int(getWordsStmt, 1, wordid);
if( sqlite3_step(getWordsStmt) != SQLITE_ROW)
{
NSLog(#"Error while getting data. '%s'", sqlite3_errmsg(database));
sqlite3_reset(getWordsStmt);
return;
}
NSString *word = [NSString stringWithUTF8String:(char *)sqlite3_column_text(getWordsStmt, 0)];
sqlite3_reset(getWordsStmt);
sqlite3_finalize(getWordsStmt);
My guess is that you are in a transaction. There must be some other calls during your open and close routines that are wrapping your calls in a transaction and not displayed in your code fragments.
That is why you don't see the new words until you application exits
There's your problem:
const char *sql_query = "SELECT word FROM words WHERE id=?";
This isn't how you use SQL. Instead, you should be using SELECT word FROM words; and stepping to get each row as long as you're getting SQLITE_ROW until you get SQLITE_DONE. That will get you all your words. How are you going to find a word by id when you don't know the id of newly added words?