Getting error in Sqlite ios Failed to open db connection? - ios

I'm building an iOS app using storyboards.I have integrated sqlite database in my app.
I'm unable to insert data into the table,i'm getting this error:
Failed to open db connection
I have created twoo more table with the same code that is working fine but this sports is my third table in which i'm getting this error.
here is my code
//SQLlite database code used to get file path
-(NSString *) getSportsFilePath {
NSString * docsPath= NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES)[0];
return [docsPath stringByAppendingPathComponent:#"sportsdb.db"];
}
-(int) createTable:(NSString*) filePath {
sqlite3* db = NULL;
int rc=0;
rc = sqlite3_open_v2([filePath cStringUsingEncoding:NSUTF8StringEncoding], &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
if (SQLITE_OK != rc) {
sqlite3_close(db);
}
else {
char * query ="CREATE TABLE IF NOT EXISTS sportsselection (id INTEGER PRIMARY KEY AUTOINCREMENT, sportslist TEXT)";
char * errMsg;
rc = sqlite3_exec(db, query,NULL,NULL,&errMsg);
if (SQLITE_OK != rc) {
NSLog(#"Failed to create table rc:%d, msg=%s",rc,errMsg);
}
sqlite3_close(db);
}
return rc;
}
//SQLlite database code is used to insert data into the table
-(int) insert:(NSString *)filePath withName:(NSString *)sportslist {
sqlite3* db = NULL;
int rc=0;
rc = sqlite3_open_v2([filePath cStringUsingEncoding:NSUTF8StringEncoding], &db, SQLITE_OPEN_READWRITE , NULL);
if (SQLITE_OK != rc) {
sqlite3_close(db);
NSLog(#"Failed to open db connection");
}
else {
NSString * query = [NSString stringWithFormat:#"INSERT INTO sportsselection (sportslist) VALUES (\"%#\")",sportslist];
char * errMsg;
rc = sqlite3_exec(db, [query UTF8String] ,NULL,NULL,&errMsg);
if (SQLITE_OK != rc) {
NSLog(#"Failed to insert record rc:%d, msg=%s",rc,errMsg);
}
sqlite3_close(db);
}
return rc;
}
- (void)viewDidLoad {
if ( _sports) {
for (int j=0;j< _sports.count;j++) {
int rc= [self insert:[self getSportsFilePath] withName: _sports[j]];
if (rc != SQLITE_OK) {
NSLog(#"Failed to insert record");
}
else
NSLog(#"Record is added");
}
}
}

You should log the return code that sqlite3_open_v2 returned to determine the cause of the error.
One way you could get the error you describe would be if you failed to call createTable before you called insert. Usually you would check for the existence of the file, and call createTable if it's not found. You don't appear to call createTable anywhere.

Related

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.

Multiple tables not getting created in sqllite3 + iOS

I am developing an iOS application in objective-c and i am using sqllite3 as local DB to hold some data.
There are multiple tables that I need to use but it not happening. If I try to create multiple tables in same DB {db path as DB1} then the second table is not created.
But if I try to create different tables in different db path, then it functions normally. ex : db path as DB1 for table T1 , DB2 for table T2 and so on ....
I am not able to understand Why is this happening!
Here is the code :
Statement to create table :
if(![[NSFileManager defaultManager] fileExistsAtPath:[self getEventDbFilePath]]) //if the file does not exist
{
[self createTable:[self getEventDbFilePath]];
}
db path function :
-(NSString *) getUserInfoDbFilePath
{
NSString * docsPath= NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSLog(#"docpath=%#",docsPath);
return [docsPath stringByAppendingPathComponent:#"userinfo.db"];
}
create table
-(int) createUserInfoTable:(NSString*) filePath
{
sqlite3* db = NULL;
int rc=0;
rc = sqlite3_open_v2([filePath cStringUsingEncoding:NSUTF8StringEncoding], &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
if (SQLITE_OK != rc)
{
sqlite3_close(db);
NSLog(#"Failed to open db connection");
}
else
{
char * query ="CREATE TABLE IF NOT EXISTS userinfo (id INTEGER PRIMARY KEY AUTOINCREMENT, selfuserid TEXT, profilepic TEXT, username TEXT, gender TEXT, profilelink TEXT )";
char * errMsg;
rc = sqlite3_exec(db, query,NULL,NULL,&errMsg);
if(SQLITE_OK != rc)
{
NSLog(#"Failed to create table rc:%d, msg=%s",rc,errMsg);
}
sqlite3_close(db);
}
return rc;
}
If I create a new table with similar function as above and different table name, with same db path, the second table will not be created.
Now for second table, if I provide a new path, the table gets created in thew db path.
Any suggestions would be great.

Objective C SQLite insert statements not working database is locked

I need to figure out why I am getting the "database is locked" error.
Here is my code:
-(void)insertQuery:(NSString*)query db:(sqlite3*)db
{
sqlite3_stmt *stmnt = NULL;
sqlite3_prepare_v2(db, [query UTF8String], -1, &stmnt, NULL);
if (sqlite3_step(stmnt) != SQLITE_DONE)
{
NSLog(#"ERROR: %s", sqlite3_errmsg(db));
}
sqlite3_finalize(stmnt);
sqlite3_close(db);
}
Before this method is used I create an sqlite3 object like so:
sqlite3 *db = NULL;
int rc = 0;
rc = sqlite3_open_v2([[self getDbFilePath] cStringUsingEncoding:NSUTF8StringEncoding], &db, SQLITE_OPEN_READWRITE, nil);
if (rc != SQLITE_OK)
{
sqlite3_close(db);
NSLog(#"Unable to open");
}
I don't seem to have a problem opening the database, but I do get the error message from my NSLog in the insertQuery method that says "database is locked."
The query NSString looks like this:
INSERT INTO Properties (ID,Name) VALUES (1234,"A Name")
I ended up reworking some of my code. When I create my SQLite class I use the same sqlite3 variable throughout instead of making a new one and opening and closing it in every method. I just keep the sqlite3 open the entire time.

Error not an error sqlite database while attaching database

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
}

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