Weird *char change with sqlite3_step(statement) - iphone

WIth this code I'm trying to get SQL CREATE statement from sqlite_master for a specific table:
[SQLStatement setString:#"SELECT name, sql FROM sqlite_master WHERE type='table' AND name='"];
[SQLStatement appendString:tableName];
[SQLStatement appendString:#"'"];
int dbrc;
const char *charSQLStatement = [SQLStatement UTF8String];
sqlite3_stmt *statement = nil;
dbrc = sqlite3_prepare_v2 (dbHandleSQLite, charSQLStatement, -1, &statement, NULL);
const char *nameColumn,*sqlColumn;
dbrc = sqlite3_step (statement);
NSString *tempString;
do {
nameColumn = sqlite3_column_text(statement,0);
sqlColumn = sqlite3_column_text(statement,1);
NSLog(#"%s",nameColumn);
NSLog(#"%s",sqlColumn);
tempString = [NSString stringWithCString:sqlColumn encoding:NSASCIIStringEncoding];
} while (sqlite3_step(statement) == SQLITE_ROW);
sqlite3_finalize (statement);
NSLog(#"%s",nameColumn);
NSLog(#"%s",sqlColumn);
do-while loop is run through only once. You can see, that I have made a small workaround to get this worked and assigned value to tempString.
Reason is because if sqlite3_step(statement) is ran again, nameColumn and sqlColumn change their values although not being called again??!!
nameColumn is firt assigned the name of the table and sqlColumn is assigned "CREATE TABLE..." statement, which is OK.
But when program comes to while sentence, nameColumn changes to "index" and sqlColumn to something non-understandable, although loop is not repeated.
How can this happen? What am I doing wrong?

You're not doing anything wrong. sqlite reuses buffers. If you want to keep something you get from it, make a copy of it: a pointer's value is only guaranteed until the next sqlite3_step or sqlite3_finalize.
So your workaround is how you're supposed to handle this.
From the sqlite docs:
The pointers returned are valid until a type conversion occurs as described above, or until sqlite3_step() or sqlite3_reset() or sqlite3_finalize() is called. The memory space used to hold strings and BLOBs is freed automatically. Do not pass the pointers returned sqlite3_column_blob(), sqlite3_column_text(), etc. into sqlite3_free().

Related

sqlite3 : SQL statement to update a table not working

For update a table, I write::update set =\"%#\" WHERE =\"%#\"",col1Value,col2Value];
But,this is not working. Please help.
My code is below:
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
NSString *querySQL = [NSString stringWithFormat:#"UPDATE AssemblyAssessment SET countedunits=\"%#\" WHERE assemblyid=\"%#\"",self.SAcountedunits,self.asmbIdStr];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(ipadSites,query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"done...");
}
else
{
NSLog(#"not done...");
}
sqlite3_finalize(statement);
sqlite3_close(ipadSites);
}
You should use "?" in your prepared statement and use sqlite3_bind_text then to bind the string values, instead of using stringWithFormat and %#. This way you will prevent SQL injections and security issues in your SQL requests (e.g. in case your string contains quotes or other stuff).
For your problem, please be more precise than "it doesn't work". Does it return an error code, in which case which one? Does it return with an OK status but your db isn't working? What are the types of your variables? What are the values of your temporary variables like querySQL, did you try to put breakpoints in your code to check what is happening? Etc, etc.
what is not working exactly. You can try to print error message like this:
NSLog(#"%s",sqlite3_errmsg(ipadSites));
also, for such one-line requests you can use sqlite3_exec() function:
sqlite3_exec(ipadSites, query_stmt, NULL,NULL, NULL);
The code you posted shows you getting the path to the database file but there is no code to open the database. You need to make a call to sqlite3_open_v2 before you attempt to prepare the statement or execute the query.
It's also a bad idea to use a string format to build the query. Your query should be:
const char *query_stmt = "UPDATE AssemblyAssessment SET countedunits = ? WHERE assemblyid = ?";
Prepare that statement, then make use of the appropriate sqlite3_bind_xxx methods to bind the two values to the query. This is a much safer approach. It takes care of properly quoting and escaping the values.

Delete statement in iphone not working

I am deleting all data from the table by using below code snippet
NSString *deleteStatementNS = [NSString stringWithFormat:
#"DELETE FROM %#",[tableNames objectAtIndex:i]];
const char *prepareDelete ="DELETE FROM '?'";
const char *tbleName = [[tableNames objectAtIndex:i] UTF8String];
if (sqlite3_prepare_v2(dBase, prepareDelete, -1, &dbpreprdstmnt, NULL) == SQLITE_OK)
{
dbrc = sqlite3_bind_text(dbpreprdstmnt, 1, tbleName, -1, SQLITE_TRANSIENT);
dbrc = sqlite3_step(dbpreprdstmnt);
sqlite3_finalize(dbpreprdstmnt);
dbpreprdstmnt = NULL;
}
else
{
NSLog(#"Error %#",[NSString stringWithCString:sqlite3_errmsg(dBase) encoding:NSUTF8StringEncoding]);
}
But unfortunately the delete is not happening I am getting error as Error no such table: ?
I am not able to prepare the statement only. But if i use prepare statement like below
const char *prepareDelete =[deleteStatementNS UTF8String];
This is working absolutely fine. I am not able to bind the variable to stop SQL injection attacks.May I know the reason behind this error please. I found many places where this code snippet is reported as its is working fine.
I am not able to bind the variable to stop SQL injection attacks.
Table names cannot be bound as variables.
To avoid SQL injection attacks don't let your users specify which table names will be deleted. Make sure the table names come from a trusted source (e.g. hardcoded in your program).
In fact it's a really bad idea to delete all data in a table when the table name comes from an untrusted source. Even if you prevent SQL injection attacks, an attacker could still delete data you didn't want them to delete.

Single quote encoding issue reading from sqlite for iPhone

I have created an SQLite db for iPhone to store my data (strings).
I have some data that contains ' single quotes (i.e. don't be tense).
This was inserted into the DB using the normal '' escape for SQLite.
INSERT INTO todo(test) VALUES('don''t be tense');
When I do a select on the data in Terminal I can see the single quote in the record.
don't be tense
My problem is when I read the record in, the single quote is not there in the NSLog:
dont be tense
This is the call to read in the field:
self.text = [NSString stringWIthUTF8String:(char *)sqlite3_column_text(init_statement, 1)];
NSLog(#"%#",self.translation);
I would greatly appreciate help on how to ensure the quote is read in.
Below is the full code, if it helps:
static sqlite3_stmt *init_statement = nil;
#implementation Todo
#synthesize primaryKey,text;
- (id)initWithPrimaryKey:(NSInteger)pk database:(sqlite3 *)db {
if (self = [super init]) {
primaryKey = pk;
database = db;
// Compile the query for retrieving book data. See insertNewBookIntoDatabase: for more detail.
if (init_statement == nil) {
// Note the '?' at the end of the query. This is a parameter which can be replaced by a bound variable.
// This is a great way to optimize because frequently used queries can be compiled once, then with each
// use new variable values can be bound to placeholders.
const char *sql = "SELECT text FROM todo WHERE pk=?";
if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
}
// For this query, we bind the primary key to the first (and only) placeholder in the statement.
// Note that the parameters are numbered from 1, not from 0.
sqlite3_bind_int(init_statement, 1, primaryKey);
if (sqlite3_step(init_statement) == SQLITE_ROW) {
self.text = [NSString stringWithUTF8String:(char *)sqlite3_column_text(init_statement, 0)];
NSLog(#"%#",self.text);
} else {
self.text = #"Nothing";
}
// Reset the statement for future reuse.
sqlite3_reset(init_statement);
}
return self;
}
I fixed it.
It seems that when I was updating the database I didn't delete the old one off the simulator so it was testing with old data the entire time. Stupid mistake!
So there is no issue reading in single quotes from SQLite (probably why I could never find information about it).
Thanks to those who read and tried to help!

sqlite3_prepare_v2 character limit?

I have data stored in a sqlite3 database and have several places that I read and write data to and from the database. The problem I am running into is that if the SQL statement gets to be very long the sqlite3_prepare_v2 method returns an error.
This code works:
NSString *strSQL = #"UPDATE commandList SET displayName=?, type=?, formula=?, controlButton=?, sort=? WHERE pk=?;";
const char *sql = [strSQL UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &dehydrate_statment, NULL) != SQLITE_OK) {
NSLog(#"Error: failed to create update statement with message '%#'.", sqlite3_errmsg(database));
}
But this code errors out:
NSString *strSQL = #"UPDATE commandList SET displayName=?, type=?, formula=?, onFormula=?, offFormula=?, controlButton=?, sort=? WHERE pk=?;";
const char *sql = [strSQL UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &dehydrate_statment, NULL) != SQLITE_OK) {
NSLog(#"Error: failed to create update statement with message '%#'.", sqlite3_errmsg(database));
}
note the only difference is the 1st line.
When you say "the code errors out", you really ought to post the error. This saves us from speculating, or having to write a sample to reproduce the error you are getting.
See: http://www.sqlite.org/limits.html
SQLite does limit the maximum size of an SQL statement. (This prevents undesirable behavior when run in an embedded environment, but doesn't affect the size of values bound to the statement.)
You should't be running into this size limit, based on the code above, but it is hard to say what specifically you are running into because the sample + question doesn't stand alone.
The problem doesn't exist in the iPhone simulator so it must be something else.

iPhone SQLite commands with apostrophes

I'm writing an application for the iPhone that communicates with a SQLite database but I'm running into a small problem. Whenever I try to query information based on a condition that contains an apostrophe, no results are returned.... even if a result that matches the requested condition exists. Let me give some specifics...
SQLite Table
Row--Column1--Column2---------
Test Data - 001
User's Data - 002
Objective-C Code
//Create the sql statement
sqlite3_stmt *sqlStatement;
//Create the name of the category that will be passed in
NSString *categoryName = #"User's Data";
//Create the rest of the SQL query
NSString *sqlQuery = "SELECT * FROM theTableName WHERE Column1 = ?";
//If there are no errors in the SQL query
if (sqlite3_prepare_v2(theDatabase, sqlQuery, -1, &sqlStatement, nil) == SQLITE_OK)
{
//Bind the category name to the sql statement
sqlite3_bind_text(sqlStatement, 1, [categoryName UTF8String], -1, SQLITE_TRANSIENT);
//While there are rows being returned
while (sqlite3_step(sqlStatement) == SQLITE_ROW)
{
//Retrieve row data
}
}
else
{
//Save error message to the application log and terminate the app
NSAssert1(0,#"Error: Failed to prepare the SQL statement with message '%s'.", sqlite3_errmsg(database));
}
//Reset the sql statement
sqlite3_reset(sqlStatement);
I'm semi-new to objective C, so my first thought when writing this code was to sanitize the user inputs. But after doing some research, I read that the sqlite3_bind calls do the necessary sanitation for you. But whenever the code runs, the while loop is skipped right over because there are no rows being returned. It should return the second row from the database table. If I copy/paste the exact same SQL query into a SQL managing program (I use SQLite Manager) (and with the necessary query sanitation of course), it returns the correct data.
I've spent a long time trying to debug this myself and even a greater amount of time trying to search online for a similar problem being explained and resolved, but to no avail. As of now, I just disabled the user's ability to key in an apostrophe on the iPhone's virtual keyboard. But this is a feature I'd love to include in my finished product. Can anyone here offer me any helpful tips? Any kind of help would be greatly appreciated.
For sqlite your request will be (as you can see it is even wrong highlighted):
SELECT * FROM theTableName WHERE Column1 = User's data
And it will wait for the closing ' symbol
You should echo ' symbol, for example in following way:
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT * FROM tableName WHERE Column1=\"%#\"", categoryName];
In this case query will be
select * from theTableName where column1="User's data"
that is completely legal query.
In this case you don't need binding any more and final code will look like:
if (sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &sqlStatement, nil) == SQLITE_OK)
{
//While there are rows being returned
while (sqlite3_step(sqlStatement) == SQLITE_ROW)
{
//Retrieve row data
}
}
else
{
//Save error message to the application log and terminate the app
NSAssert1(0,#"Error: Failed to prepare the SQL statement with message '%s'.", sqlite3_errmsg(database));
}
The official character is ''
sanitize with:
NSString *stringToSanitize = #"This is the value with ' character";
NSString *sanitized = [stringToSanitize stringByReplacingOccurrencesOfString:#"'"
withString:#"''"];
Then you can use it on your querys