SQLite use UITextfield to select column header - objective-c

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.

Related

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.

Sqlite_binding methods

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

xcode sqlite delete row in one table where EXISTS in another table

When I run the following code to delete a row from the MAIN.NOTES table, if tasks.todoDone does not have a match nothing is deleted in MAIN.NOTES. But if one tasks.todoDone is greater than '' then all the rows in MAIN.NOTES are deleted even if task.todoNod is not less than nod.
The second part of the code, deleting from MAIN.TASKS works correctly and only deletes rows that match both tests.
What do I have wrong in my EXISTS (select * line?
if (sqlite3_open(dbpath, &tasksDB) == SQLITE_OK) {
NSString *querySQLNotes = [NSString stringWithFormat:#"DELETE FROM MAIN.NOTES WHERE EXISTS (select * from MAIN.TASKS where tasks.todoNod <'%i' and tasks.todoDone > '')",nod];
char *errMsg;
const char *query_stmtN = [querySQLNotes UTF8String];// this should delete the notes for task that will be deleted next
if (sqlite3_exec(tasksDB, query_stmtN, NULL, NULL, &errMsg) == SQLITE_OK) {
int tc = sqlite3_changes(tasksDB);
NSLog(#"total count of deleted notes %i",tc);
}
NSString *querySQL = [NSString stringWithFormat:#"DELETE FROM MAIN.TASKS WHERE todoNod <'%i' and todoDone > ''",nod];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(tasksDB, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
if (sqlite3_step(statement) == SQLITE_DONE) {
NSLog(#"Match found and deleted");
} else {
NSLog(#"Match found and not deleted");
}
sqlite3_finalize(statement);
int tc = sqlite3_changes(tasksDB);
NSLog(#"total count of deleted tasks %i",tc);
}
sqlite3_close(tasksDB);
}
Thanks

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)