I can't make a Select on a table in a sqlite database.
I have the following code to copy the .sqlite-file to the user's directory:
// copy the database to the user's directory
- (void)checkAndCreateDB {
// 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] pathForResource:databaseName ofType:nil];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
[fileManager release];
}
And the select here:
sqlite3 *database;
categories = [[NSMutableArray alloc] init];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement = "select * from category";
sqlite3_stmt *compiledStatement;
NSLog(#"get");
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
NSLog(#"test");
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *cId = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *cName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
Category *category = [[Category alloc] initWithId:cId name:cName];
[categories addObject:category];
[categories release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
the log shows only: "get". "test" isn't there.
I hope someone can help me.
I believe your database copy code isn't working as you are passing nil to the ofType parameter of [NSBundle pathForResource]. Try passing the correct file extension and add code to detect the success or failure of the copy operation (and make your checkAndCreateDB method return BOOL) and take it from there.
Your database select code looks OK to me, so I'm guess you have an empty database as explained in #Micheal's answer.
sqlite3_open will create a new (empty) database if the one specified doesn't exist. To make sure that this isn't what's happening, try using sqlite3_open_v2 instead and use SQLITE_OPEN_READWRITE as the flags argument. That way you'll get an error if you attempt to open a database that doesn't exist.
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 things easier.
Related
Problem: After long time running with no issues, my database is giving me a headache, it just wont stay at its place in the NSDocumentDirectory. The Database strangely disappears.
I never clear the documents-folder or delete anything. It only contains the database and saves some images in there which get downloaded if the user wants to keep them.
Has anybody an idea what could be going on?
after 3 days of struggling with this problem I can't come up with a possible solution, so please help me! :(
in my Database-Singleton I have the following init-Method
- (id)initWithName{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:kDatabaseName];
//kDatabaseName = nameOfDatabase.db
[self checkAndCreateDatabase];
return self;
}
and the checkAndCreateDatabase - method:
- (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
NSURL *urlpath = [NSURL fileURLWithPath:databasePath];
NSError *error = nil;
[urlpath setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
success = [fileManager fileExistsAtPath:databasePath];
// If the database already exists then return without doing anything
if(success && sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"database opened");
sqlite3_close(database);
return;
}
else{
sqlite3_close(database);
NSLog(#"Database was created");
}
// 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];
//NSString *databasePathFromApp = [[NSBundle mainBundle] pathForResource:databaseName ofType:#"sqlite3"];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
if ([self createTables]){ //creates tables of DB, works fine
NSLog(#"Tables were created");
} else {
NSLog(#"Database failed to create and open");
}
}
This code worked for a year straight. and suddenly when i needed to do some updates, the database was not saved anymore.
After a lot of troubleshooting I found out the database is being created in the Documents folder, but when i try to access the exact same path (cause i don't touch the variables) it disappears, with it's tables.
I tried different versions of my repository, all of them seem to have the problem. I really am getting mad. :(
Are you persisting the databasePath between app launches? In iOS 8 the DocumentsDirectory (and all the others, Caches, tmp, etc) became dynamic - - their name changes in between every app launch. So if you're storing the absolute path anywhere in your app it will be invalid the next time the app launches. If this is the case, a good way to fix it is to store the path relative to the DocumentsDirectory and whenever you need it just call
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
and append your path to that.
Help this helps.
I've gotten to the point where I can create an encrypted copy of my database with SQLCipher, now I'm trying to integrate it into my project. I've tried using the following code in my app delegate to unencrypt the database...
NSString *databasePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent: #"encrypted.db"];
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
const char* key = [#"BIGSecret" UTF8String];
sqlite3_key(db, key, strlen(key));
if (sqlite3_exec(db, (const char*) "SELECT count(*) FROM sqlite_master;", NULL, NULL, NULL) == SQLITE_OK) {
// password is correct, or, database has been initialized
NSLog(#"correct password");
} else {
// incorrect password!
NSLog(#"incorrect password");
}
Then later at the persistent store, I use the following code.
if (__persistentStoreCoordinator != nil) {
return __persistentStoreCoordinator;
}
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"encrypted.db"];
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
The first time I load the program after creating the database, I'll get a "correct password" log, but anytime after that I get an "incorrect password", but the database is still usable, which leads me to believe that the database is being overwritten or something.
CoreData doesn't work directly with SQLCipher, as it's using SQLite from the device directly. You could potentially take a look at the Encrypted Core Data project, (https://github.com/project-imas/encrypted-core-data) which uses SQLCipher and a custom NSIncrementalStore to provide similar features.
I have a simple piece of code where I connect my sqliteDb.
My sqlite3_prepare_v2 though repeatedly fails. I narrowed it down to the following piece of code:
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"xyz" ofType:#"sqlite3"];
sqLiteDb returns null. I don't know why - tried everything I could and followed many threads.
Really struggling here - please help.
Are you sure your xyz.sqlite3 file is included in the correct Target Membership? If it is not included, then it will not be copied to your bundle when building.
Try this
in.h file
NSString *databasePath;
in .m file
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:#"dbname.sqlite"];
[self checkAndCreateDatabase];
-(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];
}
This must be a stupid problem but i cant get my query to work. Its as if my database is empty (I double checked, its not). Its a simple SELECT * FROM table query. This is how i try it:
+(MyDatabase *)database{
if (database == nil) {
database = [[MyDatabase alloc] init];
}
return database;
}
- (id)init
{
self = [super init];
if (self) {
NSString *sqliteDb = [[NSBundle mainBundle] pathForResource:#"personsDB" ofType:#"sqlite3"];
if (sqlite3_open([sqliteDb UTF8String], &database) != SQLITE_OK) {
NSLog(#"Fail to open Database.");
}
}
return self;
}
and
-(NSArray *)getAllRows{
NSMutableArray *retArray = [[NSMutableArray alloc] init];
NSString *query = #"SELECT * FROM persons";
sqlite3_stmt *statement;
NSLog(#"checking SQLITE_OK?");
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) {
NSLog(#"SQLITE_OK");
while (sqlite3_step(statement) == SQLITE_ROW) {
int personID = sqlite3_column_int(statement, 0);
char *personChars = (char *) sqlite3_column_text(statement, 1);
int gender = sqlite3_column_int(statement, 2);
int related = sqlite3_column_int(statement, 3);
NSString *person = [[NSString alloc] initWithUTF8String:personChars];
MyDBInfo *info = [[MyDBInfo alloc] initWithpersonID:personID person:person gender:gender related:related ];
[retArray addObject:info];
}
sqlite3_finalize(statement);
}
return retArray;
}
I think this is all the interesting stuff.
In my Log I get checking SQLITE_OK?, but no SQLITE_OK. I'm not getting Fail to open Database. so I'm assuming that its all good there.
My Database is full and there is a table called persons. I'm very new to sqlite in iOS apps.
Thank you.
Are you sure the db is included in your bundle? The default behavior of sqlite3_open is SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, so it will be created on your device/simulator if it wasn't there already, so you won't see Fail to open Database. Use sqlite3_open_v2 if you don't want SQLITE_OPEN_CREATE. Anyway, I sometimes have added files to my xcode project and they're not automatically included in my bundle. Select the target in the top of the project navigator, click on the target, select the target in the main panel, select "Build Phases", and see if your db is included in the "Copy Bundle Resources".
Probably unrelated, but I always copy the database from the bundle to documents folder (if it's not there already).
If I don't receive the expected SQLITE_OK, I always look at my return code or log my error message and error codes (and this would probably report that the table in question was not found, which would have let you identify the problem).
Thus,
NSLog(#"%s db err '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(contactDB), sqlite3_errcode(contactDB));
I have problems with SQLite function sqlite3_prepare_v2, it always returns 1 error code. I'm using SQLite wrapper SQLitemanagerforIOS4. Previously the same error happened without using the wrapper, I switched to it because, despite the statement was encoded in UTF8, the error still happened. I checked the database path with the debugger, and it's correct, so I'm lost...By the way, database is correctly opened and closed.
Here it is the pice of code :
- (NSArray *)getRowsForQuery:(NSString *)sql {
NSMutableArray *resultsArray = [[NSMutableArray alloc] initWithCapacity:1];
if (db == nil) {
[self openDatabase];
}
sqlite3_stmt *statement;
const char *query = [sql UTF8String];
int prepareStatus = sqlite3_prepare_v2(db, query, -1, &statement, NULL);
while (sqlite3_step(statement) == SQLITE_ROW) {
Many thanks for your help.
Here they are the parameters passed to the wrapper object:
- (void)viewDidLoad
{
[super viewDidLoad];
dbManager = [[SQLiteManager alloc] initWithDatabaseNamed:#"XLO.sqlite"];
SArray *provinciaArray = [dbManager getRowsForQuery:[NSString stringWithFormat:#"SELECT provincia FROM provincias;"]];
Thanks!
Peter, here it is:
- (NSError *) openDatabase {
NSError *error = nil;
NSString *databasePath = [self getDatabasePath];
const char *dbpath = [databasePath UTF8String];
#ifdef DEBUG
NSLog(#"SQL result: <%s>", dbpath );
#endif
int result = sqlite3_open(dbpath, &db);
if (result != SQLITE_OK) {
const char *errorMsg = sqlite3_errmsg(db);
NSString *errorStr = [NSString stringWithFormat:#"The database could not be opened: %#",[NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]];
error = [self createDBErrorWithDescription:errorStr andCode:kDBFailAtOpen];
}
return error;
}
Many thanks to all!
Because it's saying the database table does not exist, you may not be opening the database # the path that you think you are. Remember that sqlite is passive - it will create a database on first write.
Run the app in the simulator and print out the path. Then go to that path in terminal and use the sqlite cmdline to confirm the db is there and it has the tables.