REPLACE function of SQLite does not work on objective c ios - ios

I am working on SQLite but when I replace the data it adds again and again to my database, basically I want to store the alarm name, alarm type and alarm tune path, there is should be only 6 rows in NotiType because I am inserting exactly the 6 elements in every column. But the problem here is when I am replacing the data when there is change in alarm names types and tunes everyday, but the data is being inserted in every column below the and counting when ever the table is calling replace data function.
if([fileManager fileExistsAtPath:_dbPath] == YES) {
const char *dbPathagain = [_dbPath UTF8String];
if(sqlite3_open(dbPathagain, &_DB) == SQLITE_OK ) {
NSLog(#"database is open");
char *errorMessage;
const char *sql_statement = "CREATE TABLE IF NOT EXISTS NotiType(Alarm TEXT, Type TEXT, TPath TEXT)";
NSLog(#"created table success");
// sqlite3_stmt *statement = NULL;
// const char *dbPathagain = [ _dbPath UTF8String];
if([fileManager fileExistsAtPath:_dbPath] == YES) {
sqlite3_stmt *statement = NULL;
const char *dbPathagain = [_dbPath UTF8String];
if(sqlite3_open(dbPathagain, &_DB) == SQLITE_OK ) {
for(int itemIndex = 0; itemIndex < [azanst count];itemIndex++){
NSString *myname = [azanst objectAtIndex:itemIndex];
NSString *mytype = [alarmsstype objectAtIndex:itemIndex];
NSString *mytunepath = [alarmtune objectAtIndex:itemIndex];
NSString *insertSQLData = [NSString stringWithFormat:#"REPLACE INTO NotiType(Alarm, Type, TPath) VALUES (\"%#\", \"%#\",\"%#\")", myname, mytype, mytunepath];
NSLog(#"here is alarm names %#", myname);
NSLog(#"here alarm types %#", mytype);
NSLog(#"here alarm tune path %#", mytunepath);
const char *insert_statement = [insertSQLData UTF8String];
sqlite3_prepare_v2(_DB, insert_statement, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE) {
NSLog(#"data added successfully");
}
else {
NSLog(#"could not add timings");
}
}
sqlite3_finalize(statement);
sqlite3_close(_DB);
}
}
if (sqlite3_exec(_DB, sql_statement, NULL, NULL, &errorMessage) != SQLITE_OK) {
NSLog(#"failed to insert in table");
}
sqlite3_close(_DB);
}
else {
NSLog(#"failed to open db or cretate table");
NSLog(#"Database Error Message : %s", sqlite3_errmsg(_DB));
}
}
UPDATE
I am doing something like this
for (i=0, i<= ID.count, i++) {
NSString *insertSQLData = [NSString stringWithFormat:#"UPDATE NotiType SET Alarm =" + myname "," + "Type =" + mytype "," + "Tpath =" mytunepath + " where ID == i"];
}

You forgot to provide a WHERE statement :
NSString *insertSQLData = [NSString stringWithFormat:#"REPLACE INTO
NotiType(Alarm, Type, TPath) VALUES (\"%#\", \"%#\",\"%#\")", myname,
mytype, mytunepath];
you should add something like WHERE _id = %#, myAlarmUniqIdentifier to make sureyou only change the one that is beeing edited
edit : Come to think of it I also recommand using UPDATE, I'm not sure if REPLACE is what you want here..
UPDATE {table} SET {column1} = {editedObject.value1}, {column2} = {editedObject.value2} WHERE {uniqueIdField} = {editedObject.identifier};
EDIT 2 :
following your edit, update your for loop like this :
NSString *myname = [azanst objectAtIndex:i];
NSString *mytype = [alarmsstype objectAtIndex:i];
NSString *mytunepath = [alarmtune objectAtIndex:i];
NSNumber *myid = [ID objectAtIndex:i]; //<-- get the unique identifier for the item number ā€˜iā€˜
[NSString stringWithFormat:#"UPDATE NotiType SET Alarm=%#, Type=%#, Tpath=%# where ID=%#", myname, mytype, mytunepath, myid];

Related

How to get the last message by distinct userID using SQlite and iOS?

I have a "messages table" , and i want only to retrieve the "user ID" with his last message.
I tried to add "2 sql statements" inside each other , But it keeps on looping without stopping,
sqlite3_stmt *statement;
NSMutableArray * messages = [[NSMutableArray alloc]init];
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT DISTINCT FROMID , USERNAME from CHATCOMPLETE"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_chatDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
int userID = [[[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)] integerValue];
NSString *querySQL2 = [NSString stringWithFormat:
#"SELECT MESSAGE , USERNAME from CHATCOMPLETE where FROMID=\"%d\"",userID];
const char *query_stmt2 = [querySQL2 UTF8String];
if (sqlite3_prepare_v2(_chatDB,
query_stmt2, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(#"LAST MESSAGE %#",[[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)]);
sqlite3_reset(statement);
}
}
}
sqlite3_reset(statement);
}
}
return messages;
UPDATE:
This is the insert message
-(void)saveData:(NSString *)message toID:(int)toID fromID:(int)fromID isRead:(BOOL)read date:(NSDate *)date messageID:(int)messageID userName:(NSString*)userName
{
sqlite3_stmt *statement;
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
{
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO CHATCOMPLETE (MESSAGE, TOID, FROMID, READ, date, MESSAGEID, USERNAME) VALUES (\"%#\", \"%d\", \"%d\", \"%c\", \"%#\", \"%d\", \"%#\")", message, toID, fromID, read, date,messageID,userName];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(_chatDB, insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"DONE");
/* status.text = #"Contact added";
name.text = #"";
address.text = #"";
phone.text = #"";*/
} else {
// status.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(_chatDB);
}
}
This is the query to get the last message with a given fromID:
SELECT * FROM chatting WHERE fromID=9999 ORDER BY id DESC LIMIT 1
In SQLite 3.7.11 or later, the following query will return the message with the largest date for each sender:
SELECT *, MAX(date)
FROM ChatComplete
GROUP BY FromID
There are a few issues:
You have only one sqlite3_stmt variable for your two nested queries. You want a separate sqlite3_stmt for each.
You are calling sqlite3_reset. That is only used when binding new values to ? placeholders in your prepared statement, which is not applicable here. Worse, you're calling it inside your loop.
Unrelated to the problem at hand, but for each prepared statement, don't forget to call sqlite3_finalize when done looping through the results, in order to release the memory used when preparing the statements.
Thus, you might want something like:
sqlite3_stmt *userStatement;
sqlite3_stmt *messageStatement;
int rc; // the return code
NSMutableArray * messages = [[NSMutableArray alloc]init];
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_chatDB) == SQLITE_OK)
{
const char *query_stmt = "SELECT DISTINCT FROMID , USERNAME from CHATCOMPLETE";
if (sqlite3_prepare_v2(_chatDB, query_stmt, -1, &userStatement, NULL) != SQLITE_OK)
{
NSLog(#"%s: prepare userStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
}
else
{
while ((rc = sqlite3_step(userStatement)) == SQLITE_ROW)
{
int userID = [[[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)] integerValue];
const char *query_stmt2 = "SELECT MESSAGE , USERNAME from CHATCOMPLETE where FROMID=? ORDER BY timestamp DESC LIMIT 1"; // change the `ORDER BY` to use whatever field you want to sort by
if (sqlite3_prepare_v2(_chatDB, query_stmt2, -1, &messageStatement, NULL) != SQLITE_OK)
{
NSLog(#"%s: prepare messageStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
}
else
{
if (sqlite3_bind_int(messageStatement, 1, userID) != SQLITE_OK)
{
NSLog(#"%s: bind userID %d failed: %s", __PRETTY_FUNCTION__, userID, sqlite3_errmsg(_chatDB));
}
while ((rc = sqlite3_step(messageStatement)) == SQLITE_ROW)
{
NSLog(#"LAST MESSAGE %#",[[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)]);
}
if (rc != SQLITE_DONE)
{
NSLog(#"%s: step messageStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
}
sqlite3_finalize(messageStatement);
}
}
if (rc != SQLITE_DONE)
{
NSLog(#"%s: step userStatement failed: %s", __PRETTY_FUNCTION__, sqlite3_errmsg(_chatDB));
}
sqlite3_finalize(userStatement);
}
}
else
{
NSLog(#"%s: open %# failed", __PRETTY_FUNCTION__, _databasePath);
}
return messages;
Note, this code sample, in addition to my three points above, also:
Log errors using sqlite3_errmsg if sqlite3_prepare_v2 fails.
Added check on return codes from sqlite3_step, too, again logging sqlite3_errmsg if it fails.
Added log if sqlite3_open failed.
Use sqlite3_bind_int() rather building SQL using stringWithFormat. In this case, because userID is numeric, this isn't critical, but if ever using string values in your WHERE clauses, using the sqlite3_bind_text() function becomes critical, so I just wanted to show the pattern.
For example, look at your save routine and try saving a message that happens to have double quotation mark in it (e.g. I spoke with Bob and he says "hello" to you.). Your stringWithFormat construct will fail. If you use sqlite3_bind_text, it will solve that problem.
BTW, as you can see, when you add all of the proper validation of results, binding of values, etc., the code becomes a bit unwieldy. You might consider using FMDB, which greatly simplifies your SQLite Objective-C code.

while (sqlite3_step(statement) == SQLITE_ROW) Loop is not working

I had debugged code but don't know why While statement is not executing . After putting NSlog I came to know it's mismatching the result. 101 and SQLITE_ROW is equal to 100.
But query has result. If I fetch Select * from table name its returning me result and while statement executes. If I fetch result
sql_stmt = [NSString stringWithFormat:#"SELECT * FROM DeviceType WHERE upper(TypeName) = upper('%#')", deviceType];
While statement never execute so please help. don't know where is the problem.
Here is the code:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* path = [documentsDirectory stringByAppendingPathComponent:#"ClientDB.sqlite"];
NSString* sql_stmt = [self searchStringForLocalData];
BOOL found = NO;
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char* sql1 = [sql_stmt UTF8String];
sqlite3_stmt *statement;
NSNumber *typeCount;
typeCount = 0;
if (sqlite3_prepare_v2(database, sql1, -1, &statement, NULL) == SQLITE_OK)
{
NSLog(#" %d, %d ",sqlite3_step(statement),SQLITE_ROW);
while (sqlite3_step(statement) == SQLITE_ROW) {
// The second parameter indicates the column index into the result set.
int primaryKey1 = sqlite3_column_int(statement, 0);
NSNumber *typeId = [[NSNumber alloc] initWithInt:primaryKey1];
NSString *typeName =[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSString* sql_stmt1 = [NSString stringWithFormat:#"SELECT COUNT(*) FROM Devices WHERE DeviceTypeId =%d",primaryKey1];
const char *sql1 = [sql_stmt1 UTF8String];
sqlite3_stmt *statement;
NSNumber *typeCount;
typeCount = 0;
As already mentioned in a comment, the code calls sqlite3_step once for the NSLog, and then a second time in the condition of the while loop.
If your query returns exactly one record, the first sqlite3_step call will step to that record, and the second sqlite3_step call will then report (correctly) that no further results are available.
Just remove that NSLog call.
Alternatively, call sqlite3_step only once, and use the same return value for both the logging and the loop:
int rc;
...
while (1) {
rc = sqlite3_step(statement);
NSLog(#" %d, %d ", rc, SQLITE_ROW);
if (rc != SQLITE_ROW)
break;
...
}
if (rc != SQLITE_DONE)
NSLog(#"error: %s", sqlite3_errmsg(database));
Try to use the following syntax
while(sqlite3_step(stmt)!=SQLITE_DONE)
{
//do ur processing
for(i=0;i<NO_OF_COLS;i++)
{
char * tmp1=sqlite3_column_text(stmt,i);
if(tmp1!=NULL)
{
//use the value
}
else
{
//use a default
}
}
}
It shows that your table is totally empty. You may want to check your insert whether it works correctly. If you're using sqlite3_prepare_v2 to prepare your statement with some ? marks, remember to execute your query after all. Simply like this:
if(sqlite3_step(stmt) == SQLITE_DONE) NSLog(#"INSERTED");
else NSLog(#"NO INSERTED");
Note: It will display error only if your query is wrong, but not your prepared statement was executed or not.

Communication of the database

I am working with the communication of sqlite database from one viewcontroller to another, but some how i am not getting the database on the second view controller. Bellow is the code which i am using for it.:-
On First View controller
//Creating a table of MOtivational Thoughts
dirPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPath objectAtIndex:0];
Second.databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"S.H.E.D_DB"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([ filemgr fileExistsAtPath: Second.databasePath] == NO) {
const char *dbpath = [Second.databasePath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
char *errMsg;
const char *sql_stmt =
"CREATE TABLE IF NOT EXISTS THOUGHTS (ID integer ,Motivation_Thaought TEXT)";
if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
else
{
NSLog(#" created table");
}
sqlite3_close(contactDB);
} else {
}
}
// Fetching Data from table of MOtivational Thoughts
if(sqlite3_open([Second.databasePath UTF8String], &contactDB) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
int randomNumber = [self getRandomNumberBetween:0 to:6];
NSString *queryString = [NSString stringWithFormat:#"Select * FROM THOUGHTS where ID= '%d'",randomNumber];
const char* sql = [queryString UTF8String];
NSLog(#"path value : %#", Second.databasePath);
sqlite3_stmt *compiledStatement;
if (sqlite3_prepare_v2(contactDB, sql, -1, &compiledStatement, nil)==SQLITE_OK) {
// Loop through the results and add them to the feeds array
NSLog(#"Ready to enter while loop");
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSLog(#"reading");
NSString *aid = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
motivationView.text = aid;
NSLog(#"Thought of the day %#", aid);
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(contactDB);
// Do any additional setup after loading the view from its nib.
}
On Second View Controller
NSLog(#"path value : %#", databasePath);
if(sqlite3_open([databasePath UTF8String], &contactDB) == SQLITE_OK){
char *errMsg;
const char *sql_stmt =
"CREATE TABLE IF NOT EXISTS SHEDSLIST (SHED_ID integer ,SHED_Name TEXT ,SHED_Description TEXT, SHED_TIME DATETIME)";
if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table");
}
else
{
NSLog(#" created table");
}
sqlite3_close(contactDB);
} else {
}
PLease locate my error and give me sutable solution.
May be you should access the database path as self.databasePath in SecondViewController because I believe you have created a property for that. Instead of that you are trying to access some instance variable databasePath. Look at your following code in FirstViewController:
Second.databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"S.H.E.D_DB"]];

SQLite3 library routine called out of sequence

the following code give me an library routine called out of sequence error, but I can't explain me where is the problem. Any ideas ?
- (BOOL)insertProduct:(Product *)product inOrder:(Order *)order withAmount:(int)amount
{
BOOL ok = NO;
sqlite3_stmt *statement;
const char *dbpath = [_databasePath UTF8String];
if (sqlite3_open(dbpath, &_database) == SQLITE_OK)
{
NSString * insertSQL;
int amount = [self getAmountForProduct:product inOrder:order];
NSLog(#"%i", amount);
if (amount != -1)
{
insertSQL = [NSString stringWithFormat: #"UPDATE ARTICOLIPERORDINE SET quantita = %i WHERE ordine = %i AND articolo = '%#'", amount, order.idOrdine, product.codice];
}
else
{
insertSQL = [NSString stringWithFormat: #"INSERT INTO ARTICOLIPERORDINE (ordine, articolo, quantita) VALUES(%i, '%#', %i)",order.idOrdine, product.codice, 1];
}
NSLog(#"%#", insertSQL);
const char *insert_stmt = [insertSQL UTF8String];
if (sqlite3_prepare_v2(_database, insert_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_DONE)
{
ok = YES;
}
else
{
ok = NO;
NSLog(#"sqlite3 ERROR %s: %#",sqlite3_errmsg(_database), insertSQL);
}
sqlite3_finalize(statement);
sqlite3_close(_database);
}
else
{
NSLog(#"Error prepare = %s", sqlite3_errmsg(_database));
}
}
return ok;
}
The log print Error prepare = library routine called out of sequence
I have one approach.
Before this make sure that you have completed all your sql queries like connection open, close, finalize etc.
Before run your query actual into your code, execute that query into any database browser, You will see what is missing in your query.
As in my case I wrongly put the column name in query and run the code and I got the error multiple times. I review all the code carefully and execute the query into database browser and I found my mistake.

iOS SQLite insert and select not working?

I am trying to read/write to a SQLite db but I keep getting errors. I've verified that the db exists in ...iPhone Simulator/Applications/5.1/app#/Documents/kipSQLDB.db
I've done INSERT and SELECT to it from Terminal.
But from the app it just errors out. (Additional question - is there a way with sqlite to get informative error messages like mysql_error in mysql? When I error in the app I get nothing but some symbols.
Here's my code:
.h
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#interface SQLiteController : NSObject {
//file management
NSFileManager *fileManager;
NSString *documentsDirectory;
//sqlite data
sqlite3* databaseHandle;
}
- (void) initSQLiteDB;
- (void) insertData : (NSString* ) fName : (NSString* ) lName : (NSString* ) companyName;
- (NSArray* ) getData;
#end
.m
- (void) insertData : (NSString* ) fName : (NSString* ) lName : (NSString* ) companyName {
NSString* insertStatement = [NSString stringWithFormat:#"INSERT INTO nameList (userFName, userLName, userCompany) VALUES (\"%#\", \"%#\", \"%#\")", fName, lName, companyName];
NSLog(#"%#", insertStatement);
char *error;
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK) {
int recordID = sqlite3_last_insert_rowid(databaseHandle);
NSLog(#"A record was inserted into the database %# with the id of %i", databaseHandle, recordID);
} else {
NSLog(#"Error: %s", error);
}
}
- (NSArray* ) getData {
NSMutableArray* dataArray = [[NSMutableArray alloc] init];
NSString* getStatement = #"SELECT * FROM nameList";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(databaseHandle, [getStatement UTF8String], -1, &statement, NULL) == SQLITE_OK){
// Iterate over all returned rows
while (sqlite3_step(statement) == SQLITE_ROW) {
int recordID = sqlite3_column_int(statement, 0);
NSString* fNameFromDB = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
NSLog(#"%#", fNameFromDB);
}
} else {
NSLog(#"No soup for you!");
}
return 0;
}
#end
Use Try catch format and catch the error type of sql
Try this,i think it will help you some what
- (void) insertData : (NSString* ) fName : (NSString* ) lName : (NSString* ) companyName {
if(insertStatement == nil)
{
NSString* insertStatement = [NSString stringWithFormat:#"INSERT INTO nameList (userFName, userLName, userCompany) VALUES (\"%#\", \"%#\", \"%#\")", fName, lName, companyName];
NSLog(#"%#", insertStatement);
if(sqlite3_prepare_v2(database, insertSql, -1, &insertStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating insert statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_text(insertStatement, 1, [Gunameq UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(insertStatement, 2, [Gpassq UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(insertStmt))
NSAssert1(0, #"Error while inserting data. '%s'", sqlite3_errmsg(database));
else
NSLog("Inserted");
//Reset the add statement.
sqlite3_reset(insertStatement);
insertStatement= nil;
- (NSArray* ) getData {
NSMutableArray* dataArray = [[NSMutableArray alloc] init];
NSString* getStatement = #"SELECT * FROM nameList";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(databaseHandle, [getStatement UTF8String], -1, &statement, NULL) == SQLITE_OK){
// Iterate over all returned rows
while (sqlite3_step(statement) == SQLITE_ROW) {
int recordID = sqlite3_column_int(statement, 0);
NSString* fNameFromDB = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)];
NSLog(#"%#", fNameFromDB);
}
} else {
NSLog(#"No soup for you!");
}
return 0;
}
#end
SQLite can be a little tricky. All access to it must be from the same thread. For every prepare statement there must be a finalize statement.
There is an example project for using SQLite here you can refer to: https://github.com/AaronBratcher/ABSQLite
It has classes for accessing SQLite in a more traditional database way that I feel makes it easier.

Resources