Hi I'm trying to create an sqlite database in ios.
I use the method sqlite3_open with the params needed but i always get the error 14 (SQLITE_CANTOPEN 14 /* Unable to open the database file */).
It does't work even with its simplest declaration
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"data.sqlite3"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
sqlite3 *db;
if(!success)
{
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"An error has occured.");
}
else{
NSLog(#"Ok");
}
Any ideas what's happening?
Thanks.
You can't get write access to a sqlite database in your application bundle. Instead, copy the database in the bundle into the "file" system, in an appropriate place (given iCloud concerns), and then open it there. Have your app look for the database in the file system first and only copy it if it's not there.
use document directory to store the database file and then create sqlite.... it works
Related
i have created an App in where i am using a SQLlite Database...I copy that Database, if needed, in the NSCaches Directory at the first start of the app with the following method:
- (void) copyDatabaseIfNeeded {
//Using NSFileManager we can perform many file system operations.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *dbPath = [self getDBPath];
BOOL success = [fileManager fileExistsAtPath:dbPath];
if(!success) {
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"datenbankSpeed"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:dbPath error:&error];
if (!success)
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
- (NSString *) getDBPath {
//Search for standard documents using NSSearchPathForDirectoriesInDomains
//First Param = Searching the documents directory
//Second Param = Searching the Users directory and not the System
//Expand any tildes and identify home directories.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths lastObject];
return [documentsDir stringByAppendingPathComponent:#"datenbankSpeed"];
}
My problem is, if i change sth in the Database- file and create a new App File for my Customers, they install the new App over the old App but the old Database is still in use, which will result in a crash!
I'm assuming the problem is only when you change your db schema. Create a version table in the sqlite database and add a column schema_version and populate it with a value from your code. Now, whenever you change your sql schema, update schema_version number in your code. In copyDatabaseIfNeeded, check if you have existing db file, open it and read schema_version. If this version is the same as your current version, then you're fine. Else, you need to migrate the schema. You'll probably also want to migrate the data into the new schema as well.
EDIT: To clarify - in copyDatabaseIfNeeded, do the following:
int version = ... // read schema_version from db
if (version != kCurrentSchemaVersion)
{
// convert the schema into new version preserving data if required.
// this can be a multi-step process. Eg. if user directly upgrades to schema_version 3
// after schema_version 1, first you'll convert from 1->2, then 2->3.
}
You might also want to take a look at PRAGMA user_version as mentioned by #Martin in the comments.
I am using the firefox sqlite manager to help me building an iphone app. In which I include an UPDATE query, which works perfectly in the iPhone simulator. However, it fails when I run from the real machine (the iphone). There is no error, it just does not update the db.
I am thinking of two possible causes:
1) The db it updates is not the one as in simulator
2) The db I read is not the one that is updated
Does anyone have similar experience?
Code as follows:
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"Exercises.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"An error has occured.");
}
NSString *ranID = [#"UPDATE Status SET money = " stringByAppendingFormat:#"%d", money + 100];
const char *sql2 = [ranID UTF8String];
sqlite3_stmt *sqlStatement2;
if(sqlite3_prepare(db, sql2, -1, &sqlStatement2, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement 2");
}
if (sqlite3_step(sqlStatement2)==SQLITE_ROW)
NSLog(#"succeed");
Thanks in advance.
What happening is that you are trying to write inside the database that is in your bundle, you dont have write access to files in your bundle, you will need to copy it to Documents directory if you want to update the database
To copy the database file
NSString *documentsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Exercises.sqlite"];
[[NSFileManager defaultManager] copyItemAtPath:dbPath toPath:documentsPath error:NULL];
Then always use the new file path (documentsPath)to access the database
Files in your app's bundle are read-only on device.
You should copy it to somewhere in your app's sandbox first, such as in your Library/Application Support directory. (You can use - [NSFileManager URLsForDirectory:inDomains:] with NSApplicationSupportDirectory to find this path; be sure to create the directory before you try to write to it.)
This sounds like you are not committing our changes. See similar posts here and here
I'm targeting iOS 5.1 and trying to copy a database from my application files to my Documents folder. I've done this with this same code on apps in the past so I'm a little confuse as to why it isn't working this time.
This is the method I'm using to check if it exists and copy it over if it doesn't. It's from here.
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:self.databasePath];
// If the database already exists then return without doing anything
if(success) return;
NSLog(#"Installing db: %#", self.databasePath);
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
//[databasePathFromApp release];
//[fileManager release];
}
When I try to query the db I do so like this:
sqlite3 *database1;
// Open the database from the users filessytem
if(sqlite3_open([self.databasePath UTF8String], &database1) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
NSString *sqlStatement = [NSString stringWithFormat: #"update login set is_logged=0"];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database1, [sqlStatement UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK) {
if(SQLITE_DONE != sqlite3_step(compiledStatement)){
NSAssert1(0, #"Error while updating logged. '%s'", sqlite3_errmsg(database1));
NSLog(#"Error setting logged_in to 0: %s", sqlite3_errmsg(database1));
}
else{
NSLog(#"Made Logged_in=0");
}
}
else{
NSLog(#"Prop problem1: %s", sqlite3_errmsg(database1));
NSLog(#"Couldn't prep 1");
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
else{
NSLog(#"Couldn't even open db1");
}
sqlite3_close(database1);
The sqlite3_open() function returns false in this case with an sqlite3_errmsg of no such table: login.
I have a function that is called every second that creates an object that uses this database object. Could it be that the database hasn't been copied in that second and the next call is interrupting the previous copy? That doesn't sound likely.
Any ideas what might be the issue?
Solution
I consulted this question:here and as per point number 2 it seems that my database was not in the "Copy Bundle Resources" list. I added it and everything seems fine now.
Thanks for getting me thinking in the right direction guys.
Try verifying that each stage of your copy procedure returns a valid object. It could be that (for example) you are not including the extension when searching for the resource path. I tend to favor the NSBundle pathForResource:ofType function to get stuff from the bundle. Also, with regards to the empty DB, sqlite's open function creates a database if it doesn't exist, and opens that. Try getting the error from copyItemAtPath
I have an iPhone app that use an Sqlite database to store some data and some user configurations. The problem that I'm having is that when I submit an update of my application, the existing database on the user installation is overwrite with the empty database and the users lost their configurations. I'm sure it can not be too difficult to avoid this, but I don't know how to do it.
This is my code of the method that create the copy of the db:
// Creates a writable copy of the bundled default database in the application Documents directory.
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSString *writableDBPath = [self databasePath];
success = [fileManager fileExistsAtPath:writableDBPath];
if (!success) {
// The writable database does not exist, so copy the default to the appropriate location.
//NSLog(dbName);
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbName];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
}
This method is called form:
- (BOOL)openDatabase {
BOOL success = true;
if (!database) {
[self createEditableCopyOfDatabaseIfNeeded];
if (sqlite3_open([[self databasePath] UTF8String], &database) != SQLITE_OK) {
success = false;
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
}
}
return success;
}
- (NSString*)databasePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:dbName];
return path;
}
Maybe I forgot something in my code?
Can some one help me to solve this out? Thank you!
How about copying the sqlite database from your main bundle to the application's document directory, but only if it does not already exist?
If you are using Core Data, or using sqlite - you are probibly storing your data in the "Documents" directory. This will not be wiped-out when updating your app.
I don't know much about sqlite databases, except that they are in-memory databases. It is not possible to 'keep' the in-memory databases. You have two options:
1) find a way to configure your sqlite to use a file instead of running in-memory (I don't know if this is possible, I looked but couldn't find a way quickly)
2) switch to a different database provider. If the pc is yours, you can install xampp or wamp (lamp on linux), containing a pre-configured, ready-to-run MySql database.
A final way would be to temporarily store the sqlite data when exiting and then reload it on startup, but that doesn't seem very optimal!
If you don't really need a database, you could also consider alternate storing such as xml or a flatfile
I've got the following iphone code, which seems to be failing:
sqlite3_stmt *dbps;
NSString *sql = #"delete from days where day=?1;insert into days(disabled,recipe_id,day) values(?2,?3,?1)";
int rc = sqlite3_prepare_v2(db, sql.UTF8String, -1, &dbps, NULL);
...
The 'rc' return code is 1, meaning SQLITE_ERROR (SQL error or missing database, according to the sqlite site). Not sure what i've done wrong? The database 'db' is indeed open, and other queries seem to work fine.
Thanks a lot guys
Remove the insert statement from your string. It is not compiled anyway since sqlite3_prepare_v2 will "only compile the first statement in zSql."
Perhaps you should use a trigger to do your (optional) delete, or use insert or replace.
Are you sure you have copied the database in Documents directory before opening it? iPhone OS only allow write permissions in documents directory. Here is the code for copying database to Documents directory -
//function to copy database in Documents dir.
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
BOOL success;
// Create a FileManager object, we will use this to check the status
// of the database and to copy it over if required
NSFileManager *fileManager = [NSFileManager defaultManager];
// Check if the database has already been created in the users filesystem
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success) return;
// If not then proceed to copy the database from the application to the users filesystem
// Get the path to the database in the application package
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager release];
}
// open the database and fire the delete query...
sqlite3 *database;
NSString *sqlStatement = #"";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"%#",databasePath);
[serlf checkAndCreateDatabase];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
// here you can fire the delete query...
}
Silly me, i just had an old copy of the schema in my Documents folder, which didn't have the 'days' table in it. So i followed the instructions here: Cleaning up the iPhone simulator, and then it copied the new schema over, and it started working again.
Thanks for the help guys.