I have been working on an Ionic Phonegap project for iOS. There is a method implemented in Appdelegate.m which makes an AJAX request to download a text file from a server, which contains a URL to connect to another server in order for the app to work.
I have made two classes,
WebContent and WebCustomContent
In WebContent.m I insert a particular URL taken from the text file to a sqlite DB and then retrieve it using WebCustomContent.m
Refer to the following code block
-(NSString*)getDataBasePath{
//CHECK
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* foofile = [documentsPath stringByAppendingPathComponent:#"webcontentdb.sqlite"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:foofile];
NSLog(#"%d", fileExists);
//END OF CHECK
//SIMULATOR
NSString *databasePath1 = [[NSBundle mainBundle] pathForResource:#"webcontentdb" ofType:#"sqlite"];
// return databasePath1;
//REAL DEVICE
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"webcontentdb.sqlite"];
return databasePath;
}
-(void)updateUserAgeRange:(NSString*)age{
NSString* databasePath = [self getDataBasePath];
sqlite3 *database;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSString *query = [NSString stringWithFormat:#"update user_setting set valstr = '%#' where keystr = 'AGE' ", age];
NSLog(#"update %#" , query);
const char * sql = [query UTF8String];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(_database, sql, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_step(compiledStatement); // Here is the added step.
NSLog(#"updateContact SUCCESS - executed command %#",query);
}
else {
NSLog(#"updateContact FAILED - failed to execute command %#",query);
}
sqlite3_finalize(compiledStatement);
}
else {
//NSLog(#"pdateContact FAILED - failed to open database");
}
sqlite3_close(database);
}
- (NSString *)getUserPreferenceValues:(NSString*)keystr {
NSString *retval = [[NSString alloc] init] ;
NSString *query = [NSString stringWithFormat:#"SELECT valstr FROM user_setting where keystr = '%#' " , keystr];
NSLog(#" query %#", query);
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
char *nameChars = (char *) sqlite3_column_text(statement, 0 );
NSString *name = [[NSString alloc] initWithUTF8String:nameChars];
NSLog(#" valstr %#", name);
retval = name;
}
sqlite3_finalize(statement);
}
return retval;
}
-(void)insertDatabaseCommonValues:(NSString*)urlstr{
NSString* databasePath = [self getDataBasePath];
sqlite3 *database;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSString *query = [NSString stringWithFormat:#"delete from url_preference"];
const char * sql = [query UTF8String];
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(_database, sql, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_step(compiledStatement); // Here is the added step.
// NSLog(#"updateContact SUCCESS - executed command %#",query);
}
else {
//NSLog(#"updateContact FAILED - failed to execute command %#",query);
}
sqlite3_finalize(compiledStatement);
}
else {
//NSLog(#"pdateContact FAILED - failed to open database");
}
//************************************INSERT************************************//
//sqlite3 *database;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
//NSLog(#"URL STRING %#",urlstr);
NSString *query = [NSString stringWithFormat:#"insert into url_preference (name) values ( '%#' ) ", urlstr];
NSLog(#"inset %#" , query);
const char * sql = [query UTF8String];
sqlite3_stmt *compiledStatement;
NSLog(#" error code.. %d",sqlite3_prepare_v2(_database, sql, -1, &compiledStatement, NULL));
if(sqlite3_prepare_v2(_database, sql, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_step(compiledStatement); // Here is the added step.
NSLog(#"updateContact SUCCESS - executed command %#",query);
}
else {
NSLog(#"updateContact FAILED - failed to execute command %#",query);
}
sqlite3_finalize(compiledStatement);
}
else {
//NSLog(#"pdateContact FAILED - failed to open database");
}
sqlite3_close(database);
}
Here, when I print the BOOL variable fileExists, it prints YES, meaning the database exists in Documents folder.
But insertion and update queries fail as follows;
2015-06-22 11:18:18.215 App Name[5510:60b] URL http://www.google.lk
2015-06-22 11:18:22.082 App Name[5510:60b] 1
2015-06-22 11:18:24.103 App Name[5510:60b] success to open database!
2015-06-22 11:18:26.197 App Name[5510:60b] 1
2015-06-22 11:18:28.673 App Name[5510:60b] inset insert into url_preference (name) values ( 'http://www.google.lk' )
2015-06-22 11:18:28.676 App Name[5510:60b] error code.. 1
2015-06-22 11:18:28.679 App Name[5510:60b] updateContact FAILED - failed to execute command insert into url_preference (name) values ( 'http://www.google.lk' )
I've placed the database file in the project folder as shown below;
I can't seem to figure out what I'm doing wrong. Please help.
Is it possible this is a file permissions problem? I would suggest right-clicking the webcontentdb.sqlite file in Finder, select Get Info, and under Sharing & Permissions make all users have Read & Write privileges.
If that doesn't work, I would use an SQLite browser app to verify that the database file can be written to and is not corrupted in any way.
Related
I am using SqliteDatabase in my project.I am calling a function for data manuplation.
-(void)updateInspectionMapData2:(NSString *)clientid : (NSString *)inspectionid : (NSString *)status
{
NSLog(#"EIGHT");
NSLog(#"inside update data");
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSArray *checkVal = [self getSubClientDataByInspectionId:inspectionid :clientid];
NSLog(#"check is %#",checkVal);
if(checkVal == nil || [checkVal count] == 0)
{
NSString *querySql=[NSString stringWithFormat:
#"UPDATE inspectioninspectormap SET status=\"%#\" where inspectionid = \"%#\" and clientid =\"%#\" and (status = \"1\" or status = \"2\")",status,inspectionid,clientid];
NSLog(#"sql is %#",querySql);
const char *sql=[querySql UTF8String];
if(sqlite3_prepare_v2(database,sql, -1, &statement, NULL) == SQLITE_OK)
{
if(SQLITE_DONE != sqlite3_step(statement))
{
NSLog(#"Error while updating. '%s'", sqlite3_errmsg(database));
}
else
{
sqlite3_reset(statement);
NSLog(#"Update done successfully!");
}
}
sqlite3_finalize(statement);
}
}
sqlite3_close(database);
}
Please tell me is this the right way to close sqlite database.I am not sure i am right because later i get error unable to open database.?
There are many problems with your code. Here's what I see after just a quick glance:
You try to close the database even if it doesn't open.
You try to finalize the prepared statement even if the statement can't be prepared.
You needlessly call sqlite3_reset on the prepared statement.
You build your query using stringWithFormat: instead of properly binding values into the prepared statement.
You are using sqlite3_open instead of sqlite3_open_v2.
You don't log an error if sqlite3_open or sqlite3_prepare_v2 fail.
There is an issue in your code:
This code:
}
sqlite3_finalize(statement);
}
}
sqlite3_close(database);
should be changed to:
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
}
Closing the sqlite should happen right after you finish your work with database, and also within the open connection if loop, but not after the open connection!!!!
When using sqlite, opening and closing should be taken care, else it could lead to lock the database. The problem occurs when you try to open another connection to sqlite without closing the previous one, then your database will be locked .To avoid this, you need make sure that every open connection should have the close connection at the end.
You can try FMDB which is an sqlite wrapper. By using the FMDB,you can simply create the sqlite database using:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"database.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:path];
and you can open the database connection by:
[database open];
and close it by:
[database close];
and to execute a simple statement:
[database executeUpdate:#"create table user(name text primary key, age int)"];
There is a good tutorial out there:
+ (NSString*)setupDatabase
{
NSError *error;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *dbFilePath = [cachePath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", DATABASENAME, DATABASETYPE]];
if (! [[NSFileManager defaultManager] fileExistsAtPath:dbFilePath])
{
// if installing the application very first time didn't find db, need to copy
NSString *backupDbPath = [[NSBundle mainBundle] pathForResource:DATABASENAME
ofType:DATABASETYPE];
BOOL copiedBackupDb = [[NSFileManager defaultManager] copyItemAtPath:backupDbPath
toPath:dbFilePath
error:&error];
if (! copiedBackupDb)
{
// copying backup db failed
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
return nil;
}
}
return dbFilePath;
}
+ (NSString *)getDataBaseFilePath
{
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dbFilePath = [cachePath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.%#", DATABASENAME, DATABASETYPE]];
return dbFilePath;
}
+(NSString*)selectItem:(NSString*)itemID
{
NSString *name=nil;
NSString* _dataBasePath = [self getDataBaseFilePath];
sqlite3 *database;
if (sqlite3_open([_dataBasePath UTF8String], &database) == SQLITE_OK) {
NSString *query;
query= [NSString stringWithFormat:#"select ITEM_id from Table where IF ITEM_id='%#' ",itemID];
const char *sql=[query UTF8String];
sqlite3_stmt *selectstmt;
if (sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL)==SQLITE_OK) {
while (sqlite3_step(selectstmt)==SQLITE_ROW) {
if (sqlite3_column_text(selectstmt, 0))
name=[NSString stringWithUTF8String:(char*) sqlite3_column_text(selectstmt, 0)];
}
sqlite3_finalize(selectstmt);
}
}
sqlite3_close(database);
return (name) ;
}
+(BOOL)updateITEM:(ItemObj*)itemObj;
{
NSString* _dataBasePath = [self getDataBaseFilePath];
sqlite3 *database;
if (sqlite3_open([_dataBasePath UTF8String], &database) == SQLITE_OK) {
NSString *qs=[NSString stringWithFormat:#"UPDATE ITEM set User_ID = '%#',User_Name = '%#',Item_id = '%#',User_Status = '%#' WHERE Item_id = '%#'", itemObj.usersid, itemObj.user_name, itemObj.user_id, itemObj.user_status, itemObj.user_id];
const char *sql=[qs UTF8String];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) != SQLITE_OK)
return FALSE;
int result = sqlite3_step(selectstmt);
if(result != SQLITE_DONE) return FALSE;
sqlite3_finalize(selectstmt);
}
sqlite3_close(database);
return TRUE;
}
I'm creating an app for my school project that has to write data to my sqlite database. It works, as long as the app is running active but as soon as the app closes, my added data is gone and when I want to read this data this will not work off course. I included both my loadData and saveData methods. The two database paths are the same in both functions so it's not that I'm writing my data elsewhere. I really can't find the solution or the problem. I even get the insert success in my output, so the insert is successful.
- (void) saveData:(id)sender{
NSString *sqldb = [[NSBundle mainBundle] pathForResource:#"PXLate" ofType:#"sqlite3"];
sqlite3_stmt *stmt;
NSString *queryInsert = #"INSERT INTO assignments (name, lesson, dueDate, notification, start, at) VALUES ('abc','abc', 'abc', 1, 'abc', 'abc')";
NSLog(#"%#",sqldb);
NSLog(#"%#",queryInsert);
if(sqlite3_open([sqldb UTF8String], &_PXLate) == SQLITE_OK)
{
sqlite3_prepare_v2(_PXLate, [queryInsert UTF8String], -1, &stmt, NULL);
if(sqlite3_step(stmt)==SQLITE_DONE)
{
NSLog(#"insert success");
}
else
{
NSLog(#"insert un success");
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(_PXLate));
}
int success=sqlite3_step(stmt);
if (success == SQLITE_ERROR)
{
NSAssert1(0, #"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(_PXLate));
//[_PXLate save:&error];
} sqlite3_finalize(stmt);
}
sqlite3_close(_PXLate);
}
and my loadData function
- (void) loadData:(id)sender
{
//path for database
NSString *sqldb = [[NSBundle mainBundle] pathForResource:#"PXLate" ofType:#"sqlite3"];
//check if present
NSFileManager*fm=[NSFileManager defaultManager];
NSLog(#"path: %#", sqldb);
const char *dbpath = [sqldb UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_PXLate) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM assignments WHERE name='abc'", _label.text];
const char *query_stmt = [querySQL UTF8String];
NSLog(#"name");
NSLog(querySQL);
int response = sqlite3_prepare_v2(_PXLate, query_stmt, -1, &statement, NULL);
NSLog(#"response %d", response);
if (response == SQLITE_OK)
{
NSLog(#"name");
if (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *namefield = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSLog(#"name:%#", namefield);
_label.text = namefield;
} else {
_label.text = #"Match not found";
}
sqlite3_finalize(statement);
}
sqlite3_close(_PXLate);
}
}
You have to copy your sqlite to the documents directory and then work with that. Example:
self.databaseName = #"databasename.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
self.databasePath = [[NSString alloc]init];
self.databasePath = [documentsDir stringByAppendingPathComponent:self.databaseName];
[self checkAndCreateDatabase];
And the create method:
-(void)checkAndCreateDatabase
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseName];
[fileManager copyItemAtPath:databasePathFromApp toPath:self.databasePath error:nil];
}
A couple of observations:
As Retterdesdialogs said, you should
Check for existence of database in Documents;
If not there, copy from bundle to Documents; and
Open database from Documents.
You should not open database from bundle, because on the device that folder is read-only.
In your INSERT statement you are not checking the response of sqlite3_prepare_v2, which is a very common source of errors. If this is not SQLITE_OK, you should immediately log sqlite3_errmsg, before you call sqlite3_step.
You are performing sqlite3_step twice in the INSERT statement.
In loadData, you are not logging sqlite3_errmsg if sqlite3_prepare_v2 failed. Always look at sqlite3_errmsg upon any error.
I have an SQLite database for my app. To retrieve the entities from the db I use this method:
- (sqlite3*) openDatabaseNamed:(NSString*)databaseName
{
if(![databaseName isEqualToString:kTopInternationalDatabaseName] &&
![databaseName isEqualToString:kTop500RODatabaseName]){
NSAssert(nil, #"Database does not exist!");
}
sqlite3 * dataBase;
NSString * path;
path = [[NSBundle mainBundle] pathForResource:databaseName ofType:#"sqlite3"];
if (sqlite3_open([path UTF8String], &dataBase) != SQLITE_OK) {
NSString * errorString = [NSString stringWithFormat:#"[SQLITE] Unable to open database <%#> ",databaseName];
NSAssert(nil,errorString);
}
return dataBase;
}
- (NSArray *) getAllEntitiesForDatabaseNamed:(NSString*)databaseName
{
(...)
sqlite3 * database = [self openDatabaseNamed:databaseName];
NSMutableArray *retval = [[NSMutableArray alloc] init];
NSString *query = [NSString stringWithFormat:#"SELECT * FROM %#",databaseName];
NSArray *properties = [entityClass classProperties];
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
for (int i=2; i<countLimit; i++){
chars = (char *) sqlite3_column_text(statement,i+1);
if(chars != NULL)
{
containerString = [NSString stringWithUTF8String:chars];
if(containerString && containerString.length>0){
[entityModel setValue:containerString forKey:properties[i]];
}
}
}
[retval addObject:entityModel];
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return retval.copy;
}
Everything works as expected. For setting a custom field for an entity to a specific value in the database I use this method:
- (void)setEntity:(EntityModel *)entity favorite:(BOOL)favorite
{
NSString *query = [NSString stringWithFormat:#"UPDATE %# SET favorite = %i WHERE position = '%i';",kDatabaseName,favorite?1:0,entity.positionInTop];
sqlite3_stmt *statement;
sqlite3 * database = [self openDatabaseNamed:kTop500RODatabaseName];
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)
== SQLITE_OK) {
sqlite3_step(statement);
sqlite3_finalize(statement);
}
sqlite3_close(database);
}
What is happening is a little weird. If I use the update method and in the same lifecycle of the app query for all entities using getAllEntitiesForDatabaseNamed the changes I did with setEntity:Favorite: persist. On the other hand, if I use the update method, then turn off the app and restart it, the changes I did using setEntity:Favorite: are lost. Any idea on why is that?
PS: I also tried using sqlite3_exec and still the results are the same:
if (sqlite3_exec(database, [query UTF8String], NULL, NULL, NULL) != SQLITE_OK) {
// deal with error...
NSLog(#" -- ERROR!");
}
The problem is that you're opening the database in the bundle, which is read only (on a device, at least). You should, instead, check to see if the database exists in your Documents folder, if not, copy it there from the bundle, and then open the database from the Documents folder.
Thus, you might do something like:
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:databaseName ofType:#"sqlite"];
NSString *documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *documentsPath = [[documentsFolder stringByAppendingPathComponent:databaseName] stringByAppendingPathExtension:#"sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:documentsPath]) {
NSError *error = nil;
BOOL success = [fileManager copyItemAtPath:bundlePath toPath:documentsPath error:&error];
NSAssert(success, #"%s: copyItemAtPath failed: %#", __FUNCTION__, error);
}
Having done that, you can now proceed to open the database at documentsPath.
hi i'm getting stuck with some code. i have a file catalog.db and a class that let me work with it. When i try to retrieve datas from the db it seems like it's empty. With several nslog i can see that it connect and enter the database, i can see it enter in it but then it won't get any value from it.
i tried to see if the query was wrong with an external DB manager software and the query works fine...
this is my class
#import "DBAccess.h"
sqlite3* database;
#implementation DBAccess
-(id)init{
self = [super init];
if (self){
[self initializeDatabase];
}
return self;
}
-(void)initializeDatabase{
NSString *path = [[NSBundle mainBundle]pathForResource:#"catalog" ofType:#"db"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) { NSLog(#"Opening Database"); }
else {
sqlite3_close(database);
NSAssert1(0, #"FAILED to open database '%s'", sqlite3_errmsg(database));
}
}
-(void)closeDatabase{
if (sqlite3_close(database) != SQLITE_OK){
NSAssert1(0, #"ERROR to close databse: '%s'", sqlite3_errmsg(database));
}
}
-(NSMutableArray *)getAllProduct{
NSMutableArray *products = [[NSMutableArray alloc]init];
const char *sql = "SELECT product.ID,product.Name, Manufacturer.name, product.details,product.price, product.quantityOnHand,country.country,product.image FROM product,manufacturer,country WHERE manufacturer.manufacturerID = product.manufacturerID and product.countryOfOriginID = country.countryID";
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
NSLog(#"sqlResult: %d", sqlResult);
if (sqlResult == SQLITE_OK){
NSLog(#"sql step statement: %d",sqlite3_step(statement));
NSLog(#"QUERY DONE");
while (sqlite3_step(statement) == SQLITE_ROW){
NSLog(#"TEST");
Product *product = [[Product alloc]init];
char *name = (char *)sqlite3_column_text(statement, 1);
NSLog(#"n %s",name);
char *manufacturer = (char *)sqlite3_column_text(statement, 2);
NSLog(#"m %s", manufacturer);
char *details = (char *)sqlite3_column_text(statement, 3);
NSLog(#"d %s", details);
char *countryoforigin = (char *)sqlite3_column_text(statement, 6);
NSLog(#"%s", countryoforigin);
char *image = (char *)sqlite3_column_text(statement, 7);
NSLog(#"%s", image);
product.ID = sqlite3_column_text(statement, 0);
product.name = (name)?[NSString stringWithUTF8String:name]:#"";
product.manufacturer = (manufacturer)?[NSString stringWithUTF8String:manufacturer]:#"";
product.details = (details)?[NSString stringWithUTF8String:details]:#"";
product.price = sqlite3_column_double (statement, 4);
product.quantity = sqlite3_column_int(statement, 5);
product.countryOfOrigin = (countryoforigin)?[NSString stringWithUTF8String:countryoforigin]:#"";
product.image = (image)?[NSString stringWithUTF8String:image]:#"";
[products addObject:product];
}
sqlite3_finalize(statement);
}
else {
NSLog(#"Problem with database %d",sqlResult);
}
return products;
}
#end
this is what i get in my console
2013-08-14 12:38:58.505 Catalog[1642:c07] Opening Database
2013-08-14 12:38:58.508 Catalog[1642:c07] sqlResult: 0
2013-08-14 12:38:58.509 Catalog[1642:c07] sql step statement: 101
2013-08-14 12:38:58.509 Catalog[1642:c07] QUERY DONE
2013-08-14 12:38:58.510 Catalog[1642:c07] ()
what can be my problem? thanks
Here
NSLog(#"sql step statement: %d",sqlite3_step(statement));
you fetch already the first row (without using the result).
1)Copy the database to your documents directory...
-(void) checkAndCreateDatabase{
// Check if the SQL database has already been saved to the users phone, if not then copy it over
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:#"test.sqlite"];
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:#"test.sqlite"];
// Copy the database from the package to the users filesystem
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
}
2)Then talk to database in this way.
-(void)sqliteTransaction
{
sqlite3 *database;
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:#"test.sqlite"];
// Init the animals Array
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from me";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSLog(#"%#",aName);
// Create a new animal object with the data from the database
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Try like this, before going to SELECT make sure the table is properly connected for selection with the open query like this
sqlite3_open(dbpath, &ProDB)
After that execute your selection. See this below sample code. Hope it will help
ex:
NSMutableArray *sessionDetails = [NSMutableArray array];
NSString *docsDir;
NSArray *dirPaths;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"cPro.db"]];
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &ProDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"SELECT * FROM USER_SESSION_INFO"];
NSLog(#"query: %#",querySQL);
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(ProDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
// Your functionality.
}
}
}
i tried the method you guys suggested but i still get the same problem.
i think i found out what's causing it.
i changed my query with this:
const char *sql = "SELECT product.ID,product.Name, product.details,product.price, product.quantityOnHand,product.image FROM Product";
and this way it works... so it looks like i have trouble reading other tables...
the moment i add another table it stop working. but that's weird cause with external db manager software the query works just fine
I have two databases.
ch_coins.db is read-only data for tableview in [NSBundle mainBundle].
User_data.sqlite in Documents
I try to combine select from both databases and fill data to object. My method is like that:
-(NSMutableArray*)returnSubCountries
{
NSString *path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:databaseName];
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *userDB = [documentsDir stringByAppendingPathComponent:user_data];
NSMutableArray *subCountiresArr=[[NSMutableArray alloc]init];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
/*const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey\
FROM subCountries\
ORDER BY priority ASC";*/
const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey,usdb.quantity\
attach database 'userDB' as usdb\
INNER JOIN usdb on subCountries.subCountryID=usdb.refID\
FROM subCountries\
ORDER BY priority ASC";
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(database, sqlSubCountries, -1, &statement, NULL);
if ( sqlResult== SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
SubCountry *sbCountryObj=[[SubCountry alloc]init];
sbCountryObj.subCountryID=sqlite3_column_int(statement, 0);
char *subCountryName=(char *)sqlite3_column_text(statement, 1);
char *subCountryComment=(char *)sqlite3_column_text(statement, 2);
char *image=(char *)sqlite3_column_text(statement, 3);
sbCountryObj.priority=sqlite3_column_int(statement, 4);
sbCountryObj.hasRegions=(sqlite3_column_int(statement, 5)==1);
sbCountryObj.navigationKey=sqlite3_column_int(statement, 6);
sbCountryObj.quantity=sqlite3_column_int(statement, 7);
sbCountryObj.subCountryName=(subCountryName)?[NSString stringWithUTF8String:subCountryName]: #"";
sbCountryObj.subCountryComment=(subCountryComment)?[NSString stringWithUTF8String:subCountryComment]: #"";
sbCountryObj.image=(image)?[NSString stringWithUTF8String:image]: #"";
[subCountiresArr addObject:sbCountryObj];
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
}
else
{
//[self dbConnectionError];
}
return subCountiresArr;
}
I must be doing something wrong. any help is much appreciated. Thanx.
EDIT:
-(NSMutableArray*)returnSubCountries
{
NSString *path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:databaseName];
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *userDB = [documentsDir stringByAppendingPathComponent:user_data];
NSMutableArray *subCountiresArr=[[NSMutableArray alloc]init];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
/*const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey\
FROM subCountries\
ORDER BY priority ASC";*/
NSString *userDBName = [NSString stringWithFormat:#"attach database '%#' as usdb", userDB];
const char *sqlAttachedDatabase = [userDBName UTF8String];
const char *sqlSubCountries="SELECT subCountryID,subCountryName,subCountryComment,image,priority,hasRegions,navigationKey,usdb.quantity\
sqlAttachedDatabase userDB as usdb\
INNER JOIN usdb on subCountries.subCountryID=usdb.refID\
FROM subCountries\
ORDER BY priority ASC";
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(database, sqlSubCountries, -1, &statement, NULL);
if ( sqlResult== SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
SubCountry *sbCountryObj=[[SubCountry alloc]init];
sbCountryObj.subCountryID=sqlite3_column_int(statement, 0);
char *subCountryName=(char *)sqlite3_column_text(statement, 1);
char *subCountryComment=(char *)sqlite3_column_text(statement, 2);
char *image=(char *)sqlite3_column_text(statement, 3);
sbCountryObj.priority=sqlite3_column_int(statement, 4);
sbCountryObj.hasRegions=(sqlite3_column_int(statement, 5)==1);
sbCountryObj.navigationKey=sqlite3_column_int(statement, 6);
sbCountryObj.quantity=sqlite3_column_int(statement, 7);
sbCountryObj.subCountryName=(subCountryName)?[NSString stringWithUTF8String:subCountryName]: #"";
sbCountryObj.subCountryComment=(subCountryComment)?[NSString stringWithUTF8String:subCountryComment]: #"";
sbCountryObj.image=(image)?[NSString stringWithUTF8String:image]: #"";
[subCountiresArr addObject:sbCountryObj];
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
else
{
NSLog(#"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(database));
}
}
else
{
//[self dbConnectionError];
}
return subCountiresArr;
}
You're specifying the database path as userDB in your SQL, but that's not the name of the database file. That is, the name of the variable that has the full path name in it, but you presumably actually have to build your SQL with that filename. For example:
NSString *sql = [NSString stringWithFormat:#"attach database '%#' as usdb", userDB];
const char *sqlAttachedDatabase = [sql UTF8String];
Execute that. Then, as a separate statement, you can execute your SELECT SQL that uses the usdb alias. And when you're done, detach the database.
Also, I notice that you're checking to see if the result is SQLITE_OK (which is good). But you're not showing any diagnostic information if it's not. For example, your SQL is wrong, but you're not showing a meaningful error message. Thus, if your sqlite3_prepare_v2 fails to return SQLITE_OK, you should:
NSLog(#"%s: prepare failed: %s", __FUNCTION__, sqlite3_errmsg(database));
If you do this when you have an error, you'll be able to figure out what's wrong. Without that, you're flying blind.
A working example, where my "author" database is in the bundle, and my "book" database is in Documents:
int rc;
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *bookFilename = [docsPath stringByAppendingPathComponent:#"book.sqlite"];
NSString *authorFilename = [[NSBundle mainBundle] pathForResource:#"author" ofType:#"sqlite"];
sqlite3 *database;
if ((rc = sqlite3_open([authorFilename UTF8String], &database)) != SQLITE_OK)
{
NSLog(#"%s: open failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
return;
}
NSString *sql = [NSString stringWithFormat:#"attach database '%#' as userdb;", bookFilename];
if ((rc = sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL)) != SQLITE_OK)
NSLog(#"%s: attach failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
sqlite3_stmt *statement;
sql = #"select book.*, author.* from userdb.book inner join author on author_id = book_author_id;";
if ((rc = sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, NULL)) != SQLITE_OK)
NSLog(#"%s: prepare failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
while ((rc = sqlite3_step(statement)) == SQLITE_ROW)
{
// do whatever you want row by row
NSLog(#"Row");
}
if (rc != SQLITE_DONE)
NSLog(#"%s: step failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
sqlite3_finalize(statement);
sql = #"detach database userdb;";
if ((rc = sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL)) != SQLITE_OK)
NSLog(#"%s: detach failed: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), rc);
sqlite3_close(database);
You should do the following:
Run SQL "attach database 'full db path' as usdb", where "full db path" is full path to database file
Prepare and execute SELECT statement (remove "attach database ..." from it)
Detach database when it's not needed "detach database 'full db path'"