sqlite3_step return code 21 during loadView - iphone

Experimenting with sqlite, I want to init a list view with data from the DB. My source looks like this:
-(void) initTheList {
sqlite3 *db;
int rows = 0;
const char* dbFilePathUTF8 = [searchTermDBLocation UTF8String];
int dbrc = sqlite3_open( dbFilePathUTF8, &db );
// count
if (dbrc) {
NSLog( #"Could not open the DB" );
} else {
NSString *queryStatementNS = #"select count(*) from article";
const char* queryStatement = [queryStatementNS UTF8String];
sqlite3_stmt *dbps;
dbrc = sqlite3_prepare_v2(db, queryStatement, -1, &dbps, NULL);
if (sqlite3_step(dbps) == SQLITE_ROW) {
rows = sqlite3_column_int(dbps, 0);
} else {
NSLog(#"SQL step failed code: %d", sqlite3_step(dbps));
NSLog(#"Attempted Query: %#", queryStatementNS);
}
[queryStatementNS release];
}
if (rows > 1000) { ... } else { ... };
...
Actually I thought it would be good to envoke the code only one time once the view is loaded. So I added the initialization of the array and the method call to:
- (void)loadView {
[super loadView];
tableData = [[NSMutableArray alloc] initWithObjects:nil];
[self initTheList];
}
However, doing so, sqlite3_step returns x15 (SQLITE_MISUSE 21 /* Library used incorrectly */). If I place the code to invoke the method in the numberOfRowsInSection method, the call works fine.
Can somebody please give me a hint where I can learn more about the lifecycles and the relation to the sqlite database? It surprises me that I can open the DB but reading fails, while the same code placed in a method that is obviously called at a later point in time works fine.

I just ran into the error code 21 problem myself ("Library used incorrectly.") I couldn't find any helpful answers on the Web, and I was just about ready to conclude that there was some bug in the SQLite3 library in XCode. And then, at long last, I found my problem: a subtle typo in my SQL.
The error was happening during an INSERT OR REPLACE statement. But that particular statement itself had no typo, so I didn't find where I went wrong for the longest time. The database file was being created, so I ran a (Unix) strings command on it, and discovered that I had created a typo in one of the field names during the table's creation. The table created fine, but with the wrong field name. When I was doing the INSERT OR REPLACE, I was using the correct field name. Ah! The mismatch caused the error.
I don't know if this is your problem; but it is very easy to make a typo when creating SQL statements, and they aren't always easy to track down (as I just demonstrated to myself this afternoon). If I were you, I would double check every statement you have.
Hope this helps!

Today I discovered that error 21 is returned by sqlite3_step() if the prepared statement pointer happens to be NULL (zero).

Related

how to save data to different table of sqlite database

I am new in sqlite in ios. Thus, what I have to do first and next in order to save data to different tables of database?
I have used Sqlite database in my various application, it works fine for me. You have to follow these steps for inserting the data in the tables of Sqlite database ...
1.First of all you need to create your database... I hope you have created and inserted in your project and also added linked framework libsqlite3.0.dylib and import these in your classes.
2. Replace your insertion method write this code ...
+(void)insertRecord:(NSString *)dbPath record:(NSMutableArray*)dataArray{
#try{
sqlite3_stmt *insertStmnt;
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK){
for(int i=0; i<[dataArray count]; i++){
id record=[dataArray objectAtIndex:i];
NSString *insertQuery = [NSString stringWithFormat:#"INSERT INTO Table_Name (\"Column1\",\"Column2\",\"Column3\",\"Column4\",) VALUES('%#','%#','%#','%#')",[[record objectForKey:#"col1"] stringByReplacingOccurrencesOfString:#"'" withString:#"''"],[[record objectForKey:#"col2"] stringByReplacingOccurrencesOfString:#"'" withString:#"''"],[[record objectForKey:#"col3"] stringByReplacingOccurrencesOfString:#"'" withString:#"''"],[[record objectForKey:#"col4"] stringByReplacingOccurrencesOfString:#"'" withString:#"''"]];
const char *sql = [insertQuery cStringUsingEncoding:NSUTF8StringEncoding];
if(sqlite3_prepare_v2(database, sql, -1, &insertStmnt, NULL) == SQLITE_OK){
}else{
}
if(SQLITE_DONE != sqlite3_step(insertStmnt)){
}
}
}
}#catch (NSException *r) {
NSLog(#"UU %#",r);
}
}
Where record is the array of dictionary, which contains the values which you want to insert in the table or values get from the user.
stringByReplacingOccurrencesOfString:
The above method is necessary because if any user enters the data with single cote, then it will lead to your application crash.(you may remove this if you does not take input from the user)
Yes, You can also follow these links for better understanding..
1.http://www.techotopia.com/index.php/IOS_4_iPhone_Database_Implementation_using_SQLite
2.SQLite Tutorial
Hope it will work for you, if you have any query then may reply..
If you're using SQLite then go get FMDB as it will make your life alot easier. All that code above can be shrunk down to a thread safe call something like (off the top of my head, so probably not compile ready).
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdateWithFormat:#"INSERT OR REPLACE INTO table (f1,f2) VALUES (%#, %#);", field1, field2];
DLog(#"insert error: %#", [db lastErrorMessage]);
}];

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!

Weird *char change with sqlite3_step(statement)

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().

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