Error not an error sqlite database while attaching database - ios

So here's my goal: I need to attach a sync database to my main database and update or replace any fields into my main database. So I first attach my database. I then attempt to go through all the tables. Here's the quirky part: inside of my master query string, when I say: SELECT name FROM sqlite_master the if statement does not execute and says "Error: not an error." Now, when I tell the master query to SELECT name FROM sync_db.sqlite_master, the if statement executes. However, I get an error saying that no such table: sync_db.sqlite_master exists. Could someone perhaps walk me through the proper protocol? Thanks in advance.
//Atataching the sync db to the master db
NSString *attachSQL = [NSString stringWithFormat:#"ATTACH DATABASE \'%#\' AS sync_db", dbPathSync];
NSLog(#"Here's the arratch string: %#", attachSQL);
//
if ((errorNum = sqlite3_exec(mainOpenHandle, [attachSQL UTF8String], NULL, NULL, &errorMessage)) == SQLITE_OK) {
NSString *masterQuery = [NSString stringWithFormat:#"SELECT name FROM sync_db.sqlite_master WHERE type='table';"];
const char *masterStmt = [masterQuery UTF8String];
sqlite3_stmt *statement;
//If statement does not execute and prints error saying "not an error" when
//place SELECT from "sqlite_master" inside master query.
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString * currentTable = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Here's the current table: %#",currentTable);
//This is where the magic happens. If there are any keys matching the database, it will update them. If there are no current keys in the database, the query will insert them.
if ([currentTable isEqualToString:#"USER_DATA"] == NO && [currentTable isEqualToString:#"USER_ACTIVITY"]== NO && [currentTable isEqualToString:#"USER_ITINERARY"] == NO) {
NSString *tblUpdate = [NSString stringWithFormat:#"INSERT or REPLACE INTO main.%# SELECT * FROM sync_db.%#;",currentTable, currentTable];
const char *updateStmt = [tblUpdate UTF8String];
if ((errorNum = sqlite3_exec(mainOpenHandle, updateStmt, NULL, NULL, &errorMessage))!= SQLITE_OK) {
if (errorNum == 1) {
//A database reset is needded
self->isResetDataBase = YES;
}
dbErr = YES;
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
}
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(mainOpenHandle));
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
sqlite3_finalize(statement);
//Detaching the database from the mainDB
NSString *detachSQL = [NSString stringWithFormat:#"DETACH DATABASE sync_db"]; // reference sync db
if ((errorNum = sqlite3_exec(mainOpenHandle, [detachSQL UTF8String], NULL, NULL, &errorMessage))!= SQLITE_OK) {
NSLog(#"Detatched syncDb Failed. ErrorMessage = %s ", errorMessage);
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
//Closing the database when finished.
if (mainOpenHandle != nil) {
sqlite3_close(self.mainOpenHandle);
}
if (syncOpenHandle != nil) {
sqlite3_close(self.syncOpenHandle);
NSError *err;
int success = [fileManager fileExistsAtPath:dbPathSync];
if (success) {
[[NSFileManager defaultManager]removeItemAtPath:dbPathSync error: &error];
}
}
if (userOpenHandle != nil) {
sqlite3_close(self.userOpenHandle);
}
I then attempt to loop through all the rows. But here's the quirky part. Inside of

You should compare the result of sqlite3_prepare_v2 to SQLITE_OK.
When you simply do:
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)) {
then the if statement will only succeed if there is an error. You want:
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL) == SQLITE_OK) {
You should also update your code to only log errors in the else block of the if statement.
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL) == SQLITE_OK) {
// process query
} else {
// log error here
}

Related

I would like to know how to test if the compiledStatement in sqlite return an answer or not, without missing the first line

I would like to test if the SQL return a result. If there is no result, I would like to send a message. The problem is that when I do that test, the first row is skipped when I want to do my process (when rows are found).
NSString *selectSQL = [NSString stringWithFormat:#"SELECT z_pk, zpostcode, zname, zfirstname, zstreetname, zstreetnumber, zcity FROM ZTAB_PARTICULIER where UPPER(ZNAME) like \'\%%%#%%\' and ZPOSTCODE like \'\%%%#%%\'", [self.name.text uppercaseString], self.postcode.text];
const char *query_stmt = [selectSQL UTF8String];
sqlite3_stmt *compiledStatement;
//Compilation de la requete et verification du succes
if (sqlite3_prepare_v2(database, query_stmt, -1, &compiledStatement, NULL) == SQLITE_OK)
{
NSLog(#"Prepare Database OK");
int stat = sqlite3_step(compiledStatement);
if (stat == SQLITE_DONE)
{
NSLog(#"NO ROWS!");
}
while (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
...
Thanks in advance
Remove that first call to sqlite3_step. Just have a counter that you increment in your while loop, and after the while loop, just see if that counter was zero or not.
For example, you might do:
if (sqlite3_prepare_v2(database, query_stmt, -1, &compiledStatement, NULL) == SQLITE_OK) {
NSLog(#"Prepare Database OK");
// int stat = sqlite3_step(compiledStatement);
// if (stat == SQLITE_DONE)
// {
// NSLog(#"NO ROWS!");
// }
NSInteger rows = 0;
int rc;
while ((rc = sqlite3_step(compiledStatement)) == SQLITE_ROW) {
rows++;
...
}
if (rc != SQLITE_DONE) {
NSLog(#"Some error %s (%ld)", sqlite3_errmsg(), (long)rc);
}
if (rows == 0) {
NSLog(#"NO ROWS!");
}
...
Something like that checks for not only "no rows" situation, but also checks for errors.

Data are not inserting into sqliteDB iOS

I have created a table(REPORTDATA) in existing database. I am trying to insert the values in to table. But it is not inserted. I am using the following code.
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
databasePath = [docsDir stringByAppendingPathComponent:#"Album.db"];
const char *dbpath = [databasePath UTF8String];
NSString *insertSQL;
if (sqlite3_open(dbpath, & albumDB) == SQLITE_OK)
{
int rowCount = [self GetArticlesCount];
rowCount += 1;
NSString *tempcount = [NSString stringWithFormat:#"%d", rowCount];
insertSQL = [NSString stringWithFormat: #"INSERT INTO REPORTDATA (Num, Json) VALUES ('%#','%#')", tempcount, tempcount];
char *errmsg=nil;
if(sqlite3_exec(albumDB, [insertSQL UTF8String], NULL, NULL, &errmsg)==SQLITE_OK)
{
}
else
{
NSLog(#"Error Message is =%s",errmsg);
}
}
sqlite3_close(albumDB);
Get number of rows in a table:
- (int) GetArticlesCount
{
int count = 0;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
databasePath = [docsDir stringByAppendingPathComponent:#"Album.db"];
if (sqlite3_open([self.databasePath UTF8String], &albumDB) == SQLITE_OK)
{
const char* sqlStatement = "SELECT COUNT(*) FROM REPORTDATA";
sqlite3_stmt *statement;
if( sqlite3_prepare_v2(albumDB, sqlStatement, -1, &statement, NULL) == SQLITE_OK )
{
//Loop through all the returned rows (should be just one)
while( sqlite3_step(statement) == SQLITE_ROW )
{
count = sqlite3_column_int(statement, 0);
}
}
else
{
NSLog( #"Failed from sqlite3_prepare_v2. Error is: %s", sqlite3_errmsg(albumDB) );
}
sqlite3_finalize(statement);
sqlite3_close(albumDB);
}
return count;
}
I am getting
Error Message is =(null).
I'd suggest examining the actual return value of sqlite3_exec:
int rc;
char *errmsg = NULL;
if ((rc = sqlite3_exec(albumDB, [insertSQL UTF8String], NULL, NULL, &errmsg)) == SQLITE_OK) {
NSLog(#"Insert succeeded");
} else {
NSLog(#"Insert failed: %s (%ld)", errmsg, (long)rc);
if (errmsg) sqlite3_free(errmsg);
}
You report that it returned 21, which is SQLITE_MISUSE. This is typical if you called the API functions in the wrong order (e.g. performing some SQL after the database was closed).
The GetArticlesCount method is reopening a database (which is already open), replacing the albumDB variable with a new sqlite3 * pointer. Then, GetArticlesCount is closing the database, and when you return to the first method, the albumDB pointer is now referencing a closed database handle. Thus subsequent SQL calls will generate SQLITE_MISUSE.
To avoid this problem, I would advise against having each function that performs SQL from opening and closing the database. Open the database once and then have all subsequent SQLite calls use that one sqlite3 * pointer.
Try to find error by using below code.
const char *sql = "INSERT INTO REPORTDATA (Num, Json) VALUES VALUES (?,?)"
if (sqlite3_prepare_v2(albumDB, sql, -1, &statement, NULL) != SQLITE_OK)
{
NSLog(#"Prepare failure: %s", sqlite3_errmsg(albumDB));
}
if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, NULL) != SQLITE_OK)
{
NSLog(#"Bind 1 failure: %s", sqlite3_errmsg(albumDB));
}
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"Step failure: %s", sqlite3_errmsg(albumDB));
}
sqlite3_finalize(statement);
As others suggested, I also recommend you to examine the actual error message.
9 of 10, I believe it is because the database file Album.db is not in the documents directory.
Try adding a breakpoint and check the databasePath value, open that directory and confirm the file is there.
If the file has 0 bytes of size, make sure to remove it and add the correct file to your Bundle Resources in:
Project -> Targets -> right target -> Build Phases -> Copy Bundle Resources
EDIT: In your case, you closed the database in GetArticlesCount and tried to use the database pointer after closing it. So I believe Rob's answer is the right solution.

sqlite3_step(statement) == SQLITE_DONE returning false all the time

Guys I have an issue with my snippet. I must also say I'm a newbie. I'm trying to insert data in to sqlite. but I keeps failing as sqlite_step == sqlite_done returns false all the time. Am I doing something wrong here. I had done something similar before and it was working fine. following is the code
sqlite3_stmt *statement;
const char *dbpath = [_databasePath UTF8String];
if(sqlite3_open(dbpath, &_db) == SQLITE_OK){
NSString *insertSQL = [NSString stringWithFormat:#"INSERT INTO userInfo (name, email, username, password) VALUES (\"%#\",\"%#\",\"%#\",\"%#\")", self.txtName.text, self.txtEmail.text, self.txtUsername.text, self.txtPassword.text];
if([self validateRegistration])
{
const char *insert_statement = [insertSQL UTF8String];
sqlite3_prepare_v2(_db, insert_statement, -1, &statement, NULL);
if(sqlite3_step(statement) == SQLITE_DONE){
[self showUIAlertWithMessage:#"User added to the database" andTitle:#"Message"];
self.txtName.text = #"";
self.txtEmail.text = #"";
self.txtUsername.text = #"";
self.txtPassword.text = #"";
self.txtConfirmPassword.text = #"";
}else{
[self showUIAlertWithMessage:#"Failed to add the user" andTitle:#"Error"];
}
sqlite3_finalize(statement);
sqlite3_close(_db);
}
}
You must check the return value of sqlite3_prepare_v2.
If either sqlite3_prepare_v2 or sqlite3_step fails, you must get the actual error message with sqlite3_errmsg.
If you check the result of sqlite3_prepare_v2, it almost certainly is not SQLITE_OK. And if you look at sqlite3_errmsg, it will tell you precisely what is wrong:
if (sqlite3_prepare_v2(_db, insert_statement, -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"insert failed: %s", sqlite3_errmsg(_db));
Unrelated, but you should not use stringWithFormat to build your SQL. You should use ? placeholders in the SQL and then manually bind the values with sqlite3_bind_text() (or whatever).
const char *insert_statement = "INSERT INTO userInfo (name, email, username, password) VALUES (?, ?, ?, ?)";
if (sqlite3_prepare_v2(_db, insert_statement, -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"prepare failed: %s", sqlite3_errmsg(_db));
if (sqlite3_bind_text(statement, 1, [self.txtName.text UTF8String], -1, NULL) != SQLITE_OK)
NSLog(#"bind 1 failed: %s", sqlite3_errmsg(_db));
if (sqlite3_bind_text(statement, 2, [self.txtEmail.text UTF8String], -1, NULL) != SQLITE_OK)
NSLog(#"bind 2 failed: %s", sqlite3_errmsg(_db));
if (sqlite3_bind_text(statement, 3, [self.txtUsername.text UTF8String], -1, NULL) != SQLITE_OK)
NSLog(#"bind 3 failed: %s", sqlite3_errmsg(_db));
if (sqlite3_bind_text(statement, 4, [self.txtPassword.text UTF8String], -1, NULL) != SQLITE_OK)
NSLog(#"bind 4 failed: %s", sqlite3_errmsg(_db));
if(sqlite3_step(statement) == SQLITE_DONE) {
[self showUIAlertWithMessage:#"User added to the database" andTitle:#"Message"];
self.txtName.text = #"";
self.txtEmail.text = #"";
self.txtUsername.text = #"";
self.txtPassword.text = #"";
self.txtConfirmPassword.text = #"";
}else{
NSLog(#"step failed: %s", sqlite3_errmsg(_db));
[self showUIAlertWithMessage:#"Failed to add the user" andTitle:#"Error"];
}
If you find this cumbersome, I'd suggest you consider FMDB, a SQLite wrapper, that does all of the appropriate binding of values to ? placeholders for you.
I had this issue because I haven't updated my create table statement according to my insert statements as I had made some changed some values that I am inserting.
You can use sqlite3_exec() for this:
char *err;
int code = sqlite3_exec(_db,insert_statement,NULL,NULL,&err);
if (code != SQLITE_OK) {
NSLog(#"something went wrong: %s", err);
}
You then tend to use the prepare function for reading data like this:
sqlite3_stmt *stmt;
int code = sqlite3_prepare_v2(_db,_query,-1,&stmt,NULL);
if (code == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
// Retrieve data here e.g.
// int num = sqlite3_column_int(stmt, 0);
}
}
See the documentation here for sqlite3_exec()

Delete row from SQLLite database

I am trying to delete a row from my database "Code mentioned below", however it never deletes the row and the prepare statement also never seems to be called.
I can insert and update ok but for the life of me can't figure this out..
barcode is a string variable passed to the method from a editable tableview.
I can see the correct values when I log the deletSQL string but record is actually never removed.
For further info the barcode in the database is a primary key.
Also database paths are all correct and its calling the right db file.
Its just the delete function that is not working...
-(void)delete:(NSString *)barcode{
const char *dbpath = [databasePath3 UTF8String];
if (sqlite3_open(dbpath, &database3) == SQLITE_OK)
{
NSLog(#"Opened OK");
NSString *deleteSQL = [NSString stringWithFormat: #"delete from assets where assetBarcode=\"%#\'",barcode];
NSLog(#"%#",deleteSQL);
const char *sql = [deleteSQL UTF8String];
if(sqlite3_prepare_v2(database3, sql, 1, &deleteStatement, NULL) == SQLITE_OK)
{
NSLog(#"SQL OK?");
if (sqlite3_step(deleteStatement) == SQLITE_ROW)
{
sqlite3_bind_text(deleteStatement, 1, [barcode UTF8String], -1, SQLITE_TRANSIENT);
}
}
else{
NSLog(#"WHAAAATTTTT");
}
}
sqlite3_step(deleteStatement);
sqlite3_reset(deleteStatement);
sqlite3_close(database3);
}
the barcode columne in the database appears to be of type varchar
try this
-(void)delete:(NSString *)barcode {
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
if(deleteStatement == nil)
{
const char *sql = "DELETE FROM assets WHERE assetBarcode = ?";
if(sqlite3_prepare_v2(database, sql, -1, & deleteStatement, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating delete statement. '%s'", sqlite3_errmsg(database));
}
sqlite3_bind_text(deleteStatement, 1, [barcode UTF8String], -1, SQLITE_TRANSIENT);
if(SQLITE_DONE != sqlite3_step(deleteStatement))
NSAssert1(0, #"Error while deleting data. '%s'", sqlite3_errmsg(database));
else
//SQLite provides a method to get the last primary key inserted by using sqlite3_last_insert_rowid
//noteID = sqlite3_last_insert_rowid(database);
//Reset the delete statement.
sqlite3_reset(deleteStatement);
sqlite3_close(database);
deleteStatement = nil;
}
else
sqlite3_close(database);
}
Regarding the SQL string...
delete from assets where assetBarcode=\"%#\'
The parameter is being opened with a quote and closed with a single quote. Use either double or single quotes for both. Perhaps this is related to your issue?

Why do I get a SQLITE_MISUSE : Out of Memory error?

I am writing an iOS application that directly accesses SQLite. I have done this sort of thing many times on Android, so I'm struggling to see where my error lies - however my inserts are returning the SQLITE_MISUSE error (code 21), with the message "out of Memory". Below are the steps I have taken to lead me to this insert.
First, the table creation:
NSString *sql = #"CREATE TABLE IF NOT EXISTS UsersTable (lastName TEXT,id TEXT PRIMARY KEY NOT NULL,picture BLOB,firstName TEXT,age TEXT,email TEXT,sex TEXT,height TEXT,weight TEXT)";
//create the database if it does not yet exist
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: path ] == NO)
{
const char *dbpath = [path UTF8String];
//This was if (sqlite3_open(dbpath, &store) == SQLITE_OK) , but it has not made a difference.
if (sqlite3_open_v2(dbpath, &store, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK)
{
char *errMsg = NULL;
const char *sql_stmt = [sql UTF8String];
if (sqlite3_exec(store, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table: %s", errMsg);
}
if (errMsg)
sqlite3_free(errMsg);
}
else
{
NSLog(#"Failed to open/create database");
}
}
Next, the insert (currently using the email address for the user ID):
INSERT INTO UsersTable (id,lastName,firstName,email) VALUES ("jsmith#foobar.com","Smith","John","jsmith#foobar.com")
I am using one selector for all database interactions, so the above text is passed here:
-(int)execSQL:(NSString *)statement
{
NSLog(#"%#",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL);
int result = sqlite3_step(stmnt);
sqlite3_finalize(stmnt);
if (result != SQLITE_OK)
{
NSLog(#"Error: %s", sqlite3_errmsg(store));//This prints "Error: out of memory"
}
return result;
}
What am I doing wrong?
Your open routine is only creating/opening the database if the database doesn't exist. Your database probably already exists and thus your routine isn't even opening it.
Bottom line, if you try calling SQLite functions without opening the database, you will get the SQLITE_MISUSE return code (which indicates that the SQLite functions were not called in the right order) and the sqlite3_errmsg will return the cryptic "out of memory" error.
A couple of other, unrelated observations:
You really should check the return code of sqlite3_prepare as well:
- (int)execSQL:(NSString *)statement
{
int result;
NSLog(#"%#",statement);
const char *insert_stmt = [statement UTF8String];
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(#"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(#"%s: step failure: '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
}
sqlite3_finalize(stmnt);
return result;
}
In my experience, many common development problems are related to the SQL itself, something that is identified by checking the return code of the sqlite3_prepare_v2 statement.
You really should not be building your SQL statement in a NSString. You open yourself to SQL injection attacks or, considering the more benign situation, just a SQL errors if someone's name has a quotation mark in it, e.g. The "Destroyer". You should be using ? placeholders and then use sqlite3_bind_xxx functions to bind the values. Something like:
- (int)insertIdentifier:(NSString *)identifier
lastName:(NSString *)lastName
firstName:(NSString *)firstName
email:(NSString *)email
{
int result;
const char *insert_stmt = "INSERT INTO UsersTable (id, lastName, firstName, email) VALUES (?, ?, ?, ?);";
sqlite3_stmt *stmnt;
if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
{
NSLog(#"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 1, [identifier UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #1 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 2, [lastName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #2 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 3, [firstName UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #3 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_bind_text(stmnt, 4, [email UTF8String], -1, NULL)) != SQLITE_OK)
{
NSLog(#"%s: bind #4 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
sqlite3_finalize(stmnt);
return result;
}
if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
{
NSLog(#"%s: step failure: '%s'", __FUNCTION__, sqlite3_errmsg(store));
}
sqlite3_finalize(stmnt);
return result;
}
You can then call this like so:
[self insertIdentifier:#"jsmith#foobar.com"
lastName:#"Smith"
firstName:#"John"
email:#"jsmith#foobar.com"];
As you can see, as you start writing code where you're appropriately checking each and every return value, binding each variable, etc., your SQLite code gets hairy awfully quickly. I'd suggest you contemplate looking at FMDB. It's a nice, thin wrapper around the SQLite functions, which greatly simplifies the exercise of writing SQLite code in Objective-C.
You're not checking the value of the sqlite3_prepare_v2 statement. If it's not SQLITE_OK then there's an issue.
Also, does the database file already exist? If not, you need to create it or load it from the bundle.

Resources