Sqlite_binding methods - objective-c

How can I write this query without using stringWithFormat. How can I pass parameters to the SQLite Query. Now my code is this:
NSString *querySQL = [NSString stringWithFormat:#"SELECT name, char_code, sound, status From Tmy_table Where ID=\"%d\"", i];
Thanks in advance

You should use sqlite3 host parameters and sqlite3_bind() to bind variables to them. This would like something like this in your example.
NSString* query = #"SELECT name, char_code, sound, status From Tmy_table Where ID=?";
sqlite3_stmt* myStatement = NULL;
sqlite3_prepare_v2(myDBConnection, [query UTF8String], -1, &myStatement, NULL);
sqlite3_bind_int(myStatement, 1, i);
Points to note:
The two sqlite3 functions return error codes that you must check. I've left that out for clarity.
The second parameter in sqlite3_bind_int() tells you which question mar to replace with the third parameter. The index starts at 1, not 0.
See also docs about binding.

NSString sql = #"SELECT name, char_code, sound, status From Tmy_table Where ID=?";
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt, SQLITE_STATIC) == SQLITE_OK)
{
// If 'i' was text:
// if (sqlite3_bind_text(stmt, 1, i, -1, SQLITE_STATIC) == SQLITE_OK)
if (sqlite3_bind_int(stmt, 1, i) == SQLITE_OK) // Note: 1-based column when binding!!!!
{
while (sqlite3_step(stmt) == SQLITE_ROW)
{
const char *name = sqlite3_column_text(stmt, 0); // Note: 0-based column when fetching!!!
const char *sound = sqlite3_column_text(stmt, 1);
const char *status = sqlite3_column_text(stmt, 2);
// ... print the values or whatever
}
}
else
{
NSLog(#"Failed to bind int: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(stmt);
}
else
{
NSLog(#"Failed to prepare statement '%#': %s", sql, sqlite3_errmsg(database));
}
EDIT Changed the bind to sqlite3_bind_text() as i appears to be text...

Related

SQLite use UITextfield to select column header

This section of code works great. However, there is the opportunity to pick total of 3 of five search choices. With this in mind, I would like to use UITextFields for the WHERE. For example, replace "Chain = ?" replaced with "search.text = ?". I am familiar with "WHERE Chain=\"%#\"",_somthing.text" but can't work out how to replace the hard code column header with a UITextField.
Can any body please help?
NSString *querySQL = [NSString stringWithFormat:#"SELECT FullName FROM storeDetails WHERE (Chain = ? AND Format = ? AND RegionCode = ?)"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(detailspapav2, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, [_choiceText1.text UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"Bind 1 failed");
}
if (sqlite3_bind_text(statement, 2, [_choiceText2.text UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"Bind 2 failed");
}
if (sqlite3_bind_text(statement, 3, [_choiceText3.text UTF8String], -1, NULL) != SQLITE_OK) {
NSLog(#"Bind 3 failed");
}
}
If you want the column name to be dynamic, change your querySQL line to something line:
NSString *querySQL = [NSString stringWithFormat:#"SELECT FullName FROM storeDetails WHERE (%# = ? AND Format = ? AND RegionCode = ?)", search.text];
However, this is dangerous. If search.text contains a value that doesn't exactly match the name of a column in your FullName table, the query will fail.

Can't insert " character into Sqlite DB [Objective-C]

I'm inserting some data on a sqlite db, It works fine but what I noticed is that I can't insert words that contains the character ", is it a common issue? should I change parse the text and edit every " character I find?
This is the code i'm using in order to insert data into my DB:
UICollectionViewCell *cell = (UICollectionViewCell *)button.superview.superview;
NSIndexPath *indexPath = [self.customCollectionView indexPathForCell:cell];
FolderProducts *item = _feedItems[indexPath.item];
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &Carrello) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO CarrelloMese (titolo, codice, prezzo, urlImg) VALUES (\"%#\", \"%#\", \"%#\", \"%#\")",item.nomeProdotto, item.codice, item.prezzo, item.urlImg];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(Carrello, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
} else {
}
sqlite3_finalize(statement);
sqlite3_close(Carrello);
}
You need to bind your SQLite statements using the sqlite3_bind_xxx() function. Basically, you remove all variables from your statement (in your case the %#) and replace them with '?'. SQLite then knows that where an ? is HAS to be a variable, and therefore doesn't get it mixed up with a command.
For example, say you wanted to bind the word "INSERT". Using ? SQLite won't read this as a command and then flag an error.
Read the docs (link above) for full information on how to use the bind function.
Here's what your code might look like with binding (UNTESTED):
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &Carrello) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO CarrelloMese (titolo, codice, prezzo, urlImg) VALUES (?,?,?,?)"];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(Carrello, insert_stmt, -1, &statement, NULL);
if (sqlite3_bind_text(statement, 0, item.nomeProdotto.UTF8String, item.nomeProdotto.length, SQLITE_STATIC) != SQLITE_OK) {
NSLog(#"An error occurred");
}
// Etc etc
// SQLite bind works like this: sqlite_bind_text/int/e.t.c(sqlite3_stmt,index_of_variable, value);
// there are optionally parameters for text length and copy type SQLITE_STATIC and SQLITE_TRANSIENT.
if (sqlite3_step(statement) == SQLITE_DONE)
{
} else {
}
sqlite3_finalize(statement);
sqlite3_close(Carrello);
}

SQLite Case Sensitive and Case Insensitive Search

I have NSMutableArray with NSMutableDictionary that carries a SQLite search result. I need to make a difference between case-sensitive and case-insensitive search. If I read some topics here, they say SQLite by default returns a case-sensitive result. And they suggest that you use COLLATE nocase to make a case-insensitive search. Actually, with the following code, I get the same result with or without COLLATE nocase. What am I doing wrong?
const char *sql;
if (checkButtonR5.state == 1) { // checkButtonR5 is a checkbox button. If it's on, it tells a search will be case-sensitive.
sql = "Select address,date,name,age,ID From data1a WHERE request LIKE ?";
} else {
sql = "Select address,date,name,age,ID From data1a WHERE request LIKE ? COLLATE nocase";
}
sqlite3_stmt *statement; // outputFile is a file path
if (sqlite3_open([outputFile UTF8String], &connection) == SQLITE_OK){
if(sqlite3_prepare_v2(connection, sql, -1, &statement, NULL) != SQLITE_OK) {
//NSLog(#"It cannot connect the database file.");
}
NSString *bindParam = [NSString stringWithFormat:#"%%%#%%",rqt];
if(sqlite3_bind_text(statement, 1, [bindParam UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK){
//NSLog(#"Problem binding search text param.");
} else {
while (sqlite3_step(statement) == SQLITE_ROW) {
char *field0 = (char *) sqlite3_column_text(statement, 0); // address
NSString *field1Str0 = [[NSString alloc]initWithUTF8String:field0];
NSString *str0 = [[NSString alloc]initWithFormat:#"%#", field1Str0];
...
...
}
sqlite3_finalize(statement);
sqlite3_close(connection);
}
}
Thank you for your help.

Whats wrong with my SQLITE3 insert method?

Very confused by this... my "DiscoveredCars" table begins with two records in it. The first time I call this method with it inserts properly and the last line displays "3" for the number of records.
Yet the second time I call it, the SQL looks perfect yet for some reason it still is showing "3" for the size and does not seem to be inserting properly? Any ideas?
- (void)writeToDiscoveredCars: (Car *)car {
NSString *tempSQL = [NSString stringWithFormat:#"INSERT INTO DiscoveredCars (car_ID) VALUES (%i)", car.ID];
NSLog(#"size of discoveredis %i", [self numberRecordsForTable:#"DiscoveredRecipes"]);
NSLog(#"SQL insert is %#", tempSQL);
const char *sql = [tempSQL UTF8String];
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(myDatabase, sql, -1, &statement, NULL);
if (sqlResult == SQLITE_OK) {
sqlResult = sqlite3_step(statement);
if(sqlResult == SQLITE_DONE)
{
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"1. problem with database");
NSLog(#"%s", sqlite3_errmsg(myDatabase));
}
NSLog(#"size of discoveredis %i", [self numberRecordsForTable:#"DiscoveredCars"]);
}
You never execute your query by calling sqlite3_step.
int sqlResult = sqlite3_prepare_v2(myDatabase, sql, -1, &statement, NULL);
if (sqlResult == SQLITE_OK) {
sqlResult = sqlite3_step(statement); //Execute!
//check the result for completion
if(sqlResult == SQLITE_DONE)
{
}
sqlite3_finalize(statement);
}

Count Number of Rows in a SQLite Database

I'm trying the following code to count the number of rows in my SQLite database table, but it throws an exception. Is these a simpler way to do this?
- (void) countRecords {
int rows = 0;
#try {
NSString *dbPath = [self getDBPath];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *strSQL;
strSQL = #"SELECT COUNT(*) FROM MYTABLE";
const char *sql = (const char *) [strSQL UTF8String];
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK) {
// THIS IS WHERE IT FAILS:
if (SQLITE_DONE!=sqlite3_step(stmt) ) {
NSAssert1(0,#"Error when counting rows %s",sqlite3_errmsg(database));
} else {
rows = sqlite3_column_int(stmt, 0);
NSLog(#"SQLite Rows: %i", rows);
}
sqlite3_finalize(stmt);
}
sqlite3_close(database);
}
}
#catch (NSException * e) {
NSLog(#"Error Counting");
}
}
I came across a solution, using my code above, just replacing the step statement with the code below:
if (sqlite3_step(stmt) == SQLITE_ERROR) {
NSAssert1(0,#"Error when counting rows  %s",sqlite3_errmsg(database));
} else {
rows = sqlite3_column_int(stmt, 0);
NSLog(#"SQLite Rows: %i", rows);
}
This usually works for me
- (NSInteger )numberRecordsForTable:(NSString *)table {
NSInteger numTableRecords = -1;
if (sqlite3_open([self.dbPath UTF8String], &database) == SQLITE_OK) {
NSString *sqlStatement = [NSString stringWithFormat: #"select count(*) from %#", table];
const char *sql = [sqlStatement cStringUsingEncoding:NSUTF8StringEncoding];
if(sqlite3_prepare_v2(database, sql, -1, &sqlClause, NULL) == SQLITE_OK) {
while(sqlite3_step(sqlClause) == SQLITE_ROW) {
numTableRecords = sqlite3_column_int(sqlClause, 0);
}
}
else {
printf("could not prepare statement: %s\n", sqlite3_errmsg(database));
}
}
else {
NSLog(#"Error in Opening Database File");
}
sqlite3_close(database);
return numTableRecords;
}
HTH
There is no SQL expression to count rows in a database: you can count rows in a every table and then add them up.
I thought I'd trow in my two cents here as there is an expression to count rows in a database, I use it when dealing with MySQL databases using php scripts all the time. and I tested it in an ios app it's available in there too behold:
sqlite3 *database;
if(sqlite3_open([dbpath UTF8String], &database) == SQLITE_OK)
{
NSString *sql = #"select count(*) from today";
sqlite3_stmt *selectStatement;
int returnValue = sqlite3_prepare_v2(database, [sql UTF8String], -1, &selectStatement, NULL);
if (returnValue == SQLITE_OK)
{
if(sqlite3_step(selectStatement) == SQLITE_ROW)
{
numrows= sqlite3_column_int(selectStatement, 0);
}
}
sqlite3_finalize(selectStatement);
sqlite3_close(database);
}
no need for a fancy loop counter thing. btw if your using an auto increment int for the primary key. it works just slightly different then an array's key. where as in an array that is n items long the valid array elements are from 0 to n-1 in a database the key field is from 1 to n simple enough to work around if you just keep that in mind.
-(void)databaseRecordCount{
int rows = 0;
#try {
sqlite3 *database;
NSString *filePath = [self databaseDocumentsFilePath];
if(sqlite3_open([filePath UTF8String], &database) == SQLITE_OK) {
NSString *query = #"SELECT * FROM MYTABLE";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &compiledStatement, NULL) != SQLITE_OK)
NSLog(#"Error while creating detail view statement. '%s'", sqlite3_errmsg(database));
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &compiledStatement, nil) == SQLITE_OK) {
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
rows++;
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
}
#catch (NSException * e) {
NSLog(#"Error Counting");
}
NSLog(#"SQLite Rows: %i", rows);
NSUserDefaults *userDefaults;
userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:rows forKey:#"databaseRecordCount"];
[userDefaults synchronize];
}
You'll have to count of each table individually. Some pseudo code:
sql = "SELECT name FROM sqlite_master" WHERE type = 'table'
tables() = GetRows(sql)
Dim total As Integer
For Each t As String in tables
sql = "SELECT COUNT(*) FROM " + t
total = total + GetValue(sql)
Next
Show(total)