Whats wrong with my SQLITE3 insert method? - objective-c

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);
}

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);
}

Get rows from a SELECT sql statement

I have a database, and i do a S ELECT statement on SQL for 10 random rows.
It's for Iphone App, so Objective C.
How could i get back the information after the statement ?
...
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement");
}
while (sqlite3_step(sql1Statement)==SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
}
For now, i get back info just for the first row. How could i get back the info (id) for the others rows.
I would like something like that
numeroqdonnee2 =
numeroqdonnee3 =
numeroqdonnee4 =
...
Many thanks
You could add the results to some array:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *results = [NSMutableArray array];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
[results addObject:#(numeroqdonnee)];
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
// now do whatever you want with this results array
NSLog(#"results = %#", results);
// or
[results enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
NSLog(#"numeroqdonnee%d = %d", idx, [obj integerValue]);
}];
Or you could just log the results directly:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
NSLog(#"numeroqdonnee = %d", numeroqdonnee);
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
Note, I'd suggest you log the sqlite3_errmsg if you have any problems (otherwise you're just flying blind). I've also added the sqlite3_finalize (which your original code sample may have had, but I just wanted to make sure).

SQLite, Objective C: method returns int max value

-(int)countTheNumberOfDublicatesForType:(int)typeID
{
NSInteger quantity=0;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:user_data];
sqlite3* database = NULL;
sqlite3_stmt *statement;
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char *sqlQuery=sqlite3_mprintf("SELECT COUNT(refID)\
FROM dublicates\
WHERE typeREF=%i",typeID);
if(sqlite3_prepare_v2(database, sqlQuery,-1, &statement, NULL) == SQLITE_OK)
sqlite3_bind_int(statement, 1,typeID);
if(sqlite3_step(statement) == SQLITE_ROW)
{
quantity = sqlite3_column_int(statement, 0);
}
sqlite3_finalize(statement);
sqlite3_free((char*)sqlQuery);
sqlite3_close(database);
}
else
{
//
}
return quantity;
}
The math in this method returns the max value of int. Where's the mistake and how to manage it the way it would return the real values from db? Thank you in advance.
EDIT:
You have no error handling, you are binding to a nonexistent parameter, the code is indented wrong, and you are reading with a wrong data type.
Use something like this:
if (sqlite3_open([path UTF8String], &database) != SQLITE_OK) {
sqlite3_errmsg(database); // log or print this
sqlite3_close(database);
return 0;
}
if (sqlite3_prepare_v2(database,
"SELECT COUNT(refID) FROM dublicates WHERE typeREF=?",
-1, &statement, NULL) != SQLITE_OK) {
sqlite3_errmsg(database); // log or print this
sqlite3_close(database);
return 0;
}
sqlite3_bind_int(statement, 1, typeID);
if (sqlite3_step(statement) != SQLITE_ROW)
sqlite3_errmsg(database); // log or print this
else
quantity = sqlite3_column_int(statement, 0);
sqlite3_finalize(statement);
sqlite3_close(database);

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

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)