I used the following code for database creation in Xcode. It runs smoothly up to the NSFilemanager code, but after that it will terminating to else code that says status.text=#"failed to open/create database"; so table can't be created.
I imported sqlite3.h and create sqlDatabase reference variable sqlite3 still it doesn't work.
-(void)databaseCreate
{
NSString *docsDir;
NSString *dbPath;
NSArray *dirPath;
dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
NSLog(#"dirpath::%#",dirPath);
docsDir=[dirPath objectAtIndex:0];
NSLog(#"document directory::%#",docsDir);
dbPath=[[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent:#"timerpro1.db"]];
NSLog(#"database path::%#",dbPath);
NSFileManager *fileManager=[NSFileManager defaultManager];
if([fileManager fileExistsAtPath: dbPath] == NO)
{
const char *databsPath=[dbPath UTF8String];
NSLog(#"treat filemanager");
if(sqlite3_open(databsPath,&sqlDatabase) == SQLITE_OK)
{
char *err;
NSLog(#"create inside");
const char *sql_stmt="CREATE TABLE IF NOT EXISTS PRJDATA(ID INTEGER PRIMERY KEY AUTOINCREMENT,PRJ_NAME TEXT,PRJ_DATE TEXT,TIME_POINT1 TEXT,TIME_POINT2 TEXT,TIME_POINT3 TEXT,POINT2_DIFF_MIN TEXT,POINT2_DIFF_SEC TEXT,POINT3_DIFF_MIN TEXT,POINT3_DIFF_SEC TEXT)";
if (sqlite3_exec(sqlDatabase, sql_stmt, NULL, NULL, &err)!=SQLITE_OK)
{
status.text=#"failed to create table";
}
sqlite3_close(sqlDatabase);
}
else
{
status.text=#"failed to open/create database";
}
}
[fileManager release];
}
The immediate problem is that you have to replace the reference to NSDocumentationDirectory with NSDocumentDirectory.
Two asides:
When you get failures, you should examine sqlite3_errmsg(), as you'll often get descriptive errors. For example, your sqlite3_exec() statement will fail, even after you fix the NSDocumentDirectory mistake.
If you look at the sqlite3_errmsg() error message, it will tell you that you have an error near the AUTOINCREMENT clause. If you look at the SQL carefully, you'll notice that you have misspelled PRIMARY KEY. That would be more difficult to find in the absence of the sqlite3_errmsg(), which brings our attention to the particular portion of the SQL.
You can simplify dbPath declaration:
dbPath=[docsDir stringByAppendingPathComponent:#"timerpro1.db"];`
Related
It's my first time trying to develop an SQLite database for IOS, or any IOS app for that matter. I'm trying to follow a tutorial I found online and adapt it for my own use. The database was created without any issues but my Insert statement never seems to return my error message.
Nothing appears in the console as nothing drastic actually goes wrong with the program. If you need any more information I'll try my best to find it and update the question with it.
Here is my code:
// Method to store a GPS location
-(void)insertGPS:(GPS*)GPS
{
// Create insert statement for the person
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO GPSJob (jobNo, sourceMonitor, positionNumber, latitude, longitude) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\")", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude ];
// Define an error
char *error;
// Attempt to execute the insert statement
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"GPS inserted into database with values: %#, %#, %#, %#, %#.", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude);
}
// If the insert statement is not okay
else {
NSLog(#"Error: %s", sqlite3_errmsg(databaseHandle));
}
}
Here is the tutorial I'm following: http://www.apptite.be/tutorial_ios_sqlite.php
The updated error message said this :
2015-08-12 15:28:25.299 NoiseApp[7602:207] Error: out of memory
-----------------------------------Solution----------------------------------
For anyone who wants the solution to this problem I simply amended the function to read as follows :
// Method to store a GPS location
-(void)insertGPS:(GPS*)GPS
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"GPS.db"];
if(sqlite3_open([databasePath UTF8String], &databaseHandle) ==SQLITE_OK){
// Create insert statement for the person
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO GPSJob (jobNo, sourceMonitor, positionNumber, latitude, longitude) VALUES (\"%#\", \"%#\", \"%#\",\"%#\",\"%#\")", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude ];
// Define an error
char *error;
// Attempt to execute the insert statement
if ( sqlite3_exec(databaseHandle, [insertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
NSLog(#"GPS inserted into database with values: %#, %#, %#, %#, %#.", GPS.jobNumber, GPS.sourceMonitor, GPS.positionNumber, GPS.latitude, GPS.longitude);
}
// If the insert statement is not okay
else {
NSLog(#"Error: %s", sqlite3_errmsg(databaseHandle));
}
}
}
databaseHandle is not defined in your function. You should get your reference to the sqlite3_open somewhere.
Use sqlite3_open to open your database, if you didn't do this already.
Example of opening. (note if you want to write to this database you should copy it from the resourcePath to somewhere writeable, and open this version)
databaseName = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"DB.sql"];
if(sqlite3_open([databaseName UTF8String],&databaseHandle) == SQLITE_OK)
{
}
Previously I used the SQLite3 library that comes with Xcode, the code to create a database can be found below:
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"databases/database.sqlite"]];
NSLog(#"DB Path: %#", databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: databasePath ] == NO) {
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &myDatabase) == SQLITE_OK) {
char *errMsg;
const char *sql_data = "CREATE TABLE IF NOT EXISTS myTableName.....";
if (sqlite3_exec(myDatabase, sql_data, NULL, NULL, &errMsg) == SQLITE_OK) {
NSLog(#"Database OK");
[self setDatabase];
} else {
NSLog(#"Data Base Fail");
}
sqlite3_close(myDatabase);
} else {
NSLog(#"Database fail");
}
}
Most recently I had the need to encrypt a database file, and performing a brief search on the internet, I found that some sites recommend using SQL Cipher.
I implemented it in my code in the way that is described in this link, and the only thing I had to do was stay with that code (which I stated earlier), and use these two commands after sqlite3_open:
const char* keyTB = [#"MySecretPassword" UTF8String];
sqlite3_key(myDatabase, keyTB, (int)strlen(keyTB));
To check if my database was encrypted I open it with a text editor and I saw this:
øøèDEÆ?>o›$™;⁄iìÚÄ’†í]¥d=ˇÓä\Êź$4áÓÈ?ïÒ[ÅaÚvÁƒ•i%í≈ª¢.£s◊Âc®Øì≈ àÜU—–}Gec‹≥’B∂¡¸¸Æ™√3Ìnú»YÆ"ß
¬?wÚ÷fñoÂ≈ÛͯzÏâ⁄˛Ct°˘ΩfìÙº0ˇfi]
‚ŸSw∂â≤≥‘=�H€BN±HÇûß…∑º.náaߨO¬ˇ¢(B¨‹óµ¬;º‹ÀÒ
Is it really that SQL Cipher encrypted my database (256-bit AES encryption)? or need to do some configuration in my code?
It is recommended that you not directly embed the password to the database, however that is not a strict requirement for SQLCipher to work. Once you have SQLCipher integrated within your application, you do simply just need to key the database with sqlite3_key once you open the connection. To verify the state of an encrypted database, typically one will run hexdump -C on the database file itself, the content should appear indecipherable.
I am using sqlite3 in my project.
I am getting error after couple(50-60) of transaction that "unable to open
database file",So check my database file path but path is correct and
file is there.
I tried each and every solution discussed on stack overflow, but with no
luck.
I check my "DocumentDirectory" path, done all necessary step before to close database. Like:
sqlite3_finalize(selectStatement);
sqlite3_close(database);
I don't know how to tackle this problem.can I check that my sqlite3 database is open or not.
====================== 1============================
+(NSMutableArray*)executeDataSet:(NSString*)query
{
NSMutableArray *arryResult = [[NSMutableArray alloc] initWithCapacity:0];
const char *sql = [query UTF8String];
sqlite3_stmt *selectStatement;
sqlite3 *database = [DataBaseClass openDataBase];
//prepare the select statement
int returnValue = sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL);
if(returnValue == SQLITE_OK)
{
//my code
}
}
//sqlite3_reset(selectStatement);
// NILOBJECT(selectStatement);
// NILOBJECT(selectStatement);
sqlite3_finalize(selectStatement);
sqlite3_close(database);
return arryResult;
}
==================== 2 =================================
+(sqlite3 *) openDataBase {
sqlite3 * edenAdultDatabase;
NSString * databasePath =[DataBaseClass pathForDatabase];
if(sqlite3_open([databasePath UTF8String], &edenAdultDatabase) == SQLITE_OK) {
NSLog(#"Yes database is open");
return edenAdultDatabase;
}
else
{
NSLog(#"do something %s",sqlite3_errmsg(edenAdultDatabase));
}
return edenAdultDatabase;
}
====================== 3 ===========================
+(NSString *) pathForDatabase {
NSString *libraryDir = [FileManager pathForPrivateDocumentsFolder];
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSError *error;
NSString *privateFolderPath = [libraryDir stringByAppendingPathComponent:#"DataBase"];
if (![fileMgr fileExistsAtPath:privateFolderPath])
{
[fileMgr createDirectoryAtPath:privateFolderPath withIntermediateDirectories:NO attributes:nil error:&error];
}
/*
// My database in library private folder ..this is just for test.
// I copied databae to document dir but no luck.
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
privateFolderPath = [documentsDir stringByAppendingPathComponent:kDatabaseName];
*/
privateFolderPath = [privateFolderPath stringByAppendingPathComponent:kDatabaseName];
return privateFolderPath;
}
There is a weird issue in database connectivity, sometime it does not connect. Therefore it is recommended by people that your application should open a database once (during in initialisation phase) and close the connection when application is terminating.
Reference: Sqlite opening issue
Regarding checking database connectivity, Sqlite3 does not provide any method to check that either database is open or not.
By using Shared instance of database manager, you can achieve it. Define a boolean at class level and set it's value when you open the database:
// .h file
BOOL isDatabaseOpen;
// .m file
-(void) openDatabase
{
if(![self isDatabaseExist])
{
// copy database to library
[self copyDatabaseFile];
}
NSString *sqLiteDb = [self getDatabaseLibraryPath];
if (sqlite3_open([sqLiteDb UTF8String], &_databaseHandler) != SQLITE_OK) {
NSLog(#"Database --> Failed to open");
isDatabaseOpen = NO;
}
else
{
isDatabaseOpen = YES;
}
}
and then you can use the following method to check is database opened or not.
-(BOOL) isDatabaseOpen
{
return isDatabaseOpen;
}
Let me know if it worked :).
check out this kind of solution
first of all create function like below:
-(void)checkDBAndCopy{
NSArray *dirPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *connectionPath=[dirPath objectAtIndex:0];
strDBPath=[connectionPath stringByAppendingPathComponent:#"database.sqlite"];
NSLog(#"%#",strDBPath);
NSFileManager *filemanager=[[NSFileManager alloc]init];
if (![filemanager fileExistsAtPath:strDBPath]) {
NSString *databasePathFromApp=[[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:#"database.sqlite"];
[filemanager copyItemAtPath:databasePathFromApp toPath:strDBPath error:nil];
}
}
and call this function like below method:-
-(NSMutableArray *)RetriveSharedspots:(NSString *)Query{
[self checkDBAndCopy];
if (sqlite3_open([strDBPath UTF8String], &contactDB)==SQLITE_OK) {
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(contactDB, [Query UTF8String],-1,&statement,NULL)==SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
// Your code
}
}
sqlite3_finalize(statement);
}
sqlite3_close(databaseName);
return array;
}
Above this worked for me great. try this.
Just sharing my case with this issue.
I have a project that uses databaseFile1.sqlite and I am not sure if there was a build of it installed on my simulator.
Then I changed the database file, say databaseFile2.sqlite different contents, different filename. Then this issue came up. As I read the solutions and comments, I realized that the issue shouldn't be so biggie.
Welp, I deleted the build and restarted Xcode. Voila. It's okay now. Later on, I will revert back to databaseFile1.sqlite the database, and I'll see if this issue can be reproduced.
My simulator runs fine and fast. My iphone seems to be freezing at a part where I try to create and fill a database. However i prefer to use the database from the simulator and put that on the iphone so the user doesn't have to recreate the database.
What i like to know is how can i load from the database added to the folders.
I searched a lot but either it is outdated or different from what i want.
I added the database file now from the finder into the xcode project.
So if I'm correct I have to change _databasePath to point to wherever the file is, am I correct?
And if so where is it, the one from the code is here:
/var/mobile/Applications/65B5541A-1E73-46F6-AB5A-C5988003103E/Documents/paths.db
But that is no the one i dragged into xcode.
Also i looked at organizer, i can see there documents/paths.db but since it misses other files i also assume that that is the code created db and not the dragged in.
I tried to delete it as well but i can't select it.
can someone help?
in header:
#property (strong, nonatomic) NSString *databasePath;
#property (nonatomic) sqlite3 *pathDB;
in .m:
- (void) createDataBaseIfNotExist {
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the database file
_databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:#"paths.db"]];
NSLog(#"databasePath: %#", _databasePath);
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: _databasePath] == NO) {
const char *dbpath = [_databasePath UTF8String];
if(sqlite3_open(dbpath, &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt =
"CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
{
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table");
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
So, a couple of things:
You first need to modify the createDatabaseIfNotExist to copy from the bundle if it's not found in Documents:
- (void) createDataBaseIfNotExist {
// Get the documents database path
NSString *docsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.databasePath = [docsDir stringByAppendingPathComponent:#"paths.db"]; // always use setter when setting property's value
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:_databasePath] == NO) {
// if the database doesn't exist in documents, look for it in the bundle and copy it if found
// get the bundle database path
NSString *bundleDatabasePath = [[NSBundle mainBundle] pathForResource:#"paths" ofType:#"db"];
if (bundleDatabasePath) {
// if we successfully copied from bundle, then quit
if ([fileManager copyItemAtPath:bundleDatabasePath toPath:self.databasePath error:nil])
return;
}
// otherwise, let's proceed with creating the database
if(sqlite3_open([_databasePath UTF8String], &_pathDB) == SQLITE_OK) {
char *errMsg;
const char *sql_stmt = "CREATE TABLE IF NOT EXISTS Paths (ID INTEGER PRIMARY KEY AUTOINCREMENT, START INTEGER, END INTEGER, DISTANCE REAL, NODES TEXT)";
if (sqlite3_exec(_pathDB, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK) {
//_status.text = #"Failed to create table";
NSLog(#"Failed to create table, %s", errMsg);
sqlite3_free(errMsg); // if you're going to use that fifth parameter, you must free it when you're done
}
sqlite3_close(_pathDB);
} else {
// _status.text = #"Failed to open/create database";
NSLog(#"Failed to open/create database");
}
}
}
Second, once you've run this once on the simulator, find the database in the simulator's Documents folder to your Xcode project. The simulator's files can be found in
~/Library/Application Support/iPhone Simulator/6.1/Applications/XXX/Documents
where XXX is the cryptic identifier (e.g. 85206BA6-9D03-4F18-BB0A-3B8C25B552C4). Note, by default, the Library folder is hidden, so I go to a Terminal command line and type in the following command to show it:
chflags nohidden Library
You can then add the database back to your project by dragging from Finder to Xcode's file navigator window, at which point you'll see a window like:
Make sure to check the two highlighted checkmarks to ensure that the database will be included in the bundle.
Two final observations:
Now that you have a "copy from bundle if necessary logic", it's an interesting question whether you really want the code to create the table in code at all anymore. Personally, I always just create my databases with a nice Mac graphical SQLite tool and then copy them to my project. The only time I do programmatic creating of tables is when (a) I'm deploying an update which involves new/altered tables; and (b) the user's database might contain some key data that I don't want to simply replace with the database from the bundle.
I personally always include a configuration table in my app which contains a single row for which one of the columns is the database version. Thus, my app will open the database from documents, check the version number, and if out of date (because the user only just recently upgraded their app) then update the database. This "database version number" logic is something that you really want to get in place as part of version 1.0 of your app.
I working on an app that takes input from a text field and puts it into a string. I have a table with a field in it that I want to check the value of the string from the input against the value in the field in the database. I'm new to iOS and fairly new to SQLite.
Code:
-(IBAction)setInput:(id)sender
{
NSString *strStoreNumber;
NSString *strRegNumber;
strStoreNumber = StoreNumber.text;
strRegNumber = RegNumber.text;
lblStoreNumber.text = strStoreNumber;
lblRegNumber.text = strRegNumber;
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths lastObject];
// NSString* databasePath = [documentsDirectory stringByAppendingPathComponent:#"tblStore.sqlite"];
NSString* databasePath = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opened sqlite database at %#", databasePath);
//...stuff
}
else
{
NSLog(#"Failed to open database at %# with error %s", databasePath, sqlite3_errmsg(database));
sqlite3_close (database);
}
NSString *querystring;
// create your statement
querystring = [NSString stringWithFormat:#"SELECT strStore FROM tblStore WHERE strStore = %#;", strStoreNumber];
const char *sql = [querystring UTF8String];
NSString *szStore = nil;
NSString *szReg = nil;
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK) //queryString = Statement
{
NSLog(#"sql problem occured with: %s", sql);
NSLog(#"%s", sqlite3_errmsg(database));
}
else
{
// you could handle multiple rows here
while (sqlite3_step(databasePath) == SQLITE_ROW) // queryString = statement
{
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(databasePath, 1)];
} // while
}
sqlite3_finalize(databasePath);
// Do something with data...
}
It gets to the line "NSLog(#"Opened sqlite database at %#", databasePath);", so it appears as though it has access to the database. However, when I run the app, I get the "NSLog(#"sql problem occured with: %s", sql);" error, which I can see in the console. Additionally, in the console, it says "No such table: tblStore".
I created the table using the Firefox add-on SQLite Manager. I added the sqlite3 library to the project. I dragged and dropped the database table I created in SQLite manager into my project, above my two AppDelegate files and my two ViewController files.
Any help or input would be greatly appreciated. Thanks!
EDIT: I have properly added the file to the project, and it appears as though the table is found now. Now I have some strange warnings, though:
"Incompatible pointer types passing 'const char *' to parameter of type 'sqlite3_stmt *' (aka 'struct sqlite3_stmt *')"
This warning appears on the following lines of code:
if (sqlite3_prepare_v2(database, sql, -1, &databasePath, NULL)!=SQLITE_OK)
while (sqlite3_step(sql) == SQLITE_ROW)
szStore = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 0)];
szReg = [NSString stringWithUTF8String:(char*)sqlite3_column_text(sql, 1)];
sqlite3_finalize(sql);
It's got something to do with "sql", but I'm unsure of what. Any suggestions?
Your code seems ok - did you copy the db to the ressource folder of your project?
EDIT
Make sure you access your db file with something like that:
- (void) initializeDB {
// Get the database from the application bundle
NSString* path = [[NSBundle mainBundle] pathForResource:#"tblStore" ofType:#"sqlite"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSLog(#"Opening Database");
}
else
{
// Call close to properly clean up
sqlite3_close(database);
NSAssert1(0, #"Error: failed to open database: '%s'.",
sqlite3_errmsg(database));
}
}
The database file you add to the project will be embedded in the main NSBundle (see [NSBundle mainBundle]).
In order to do what you want, you need to copy the database from the main bundle to the documents folder before trying to access it. Otherwise, as you are experiencing, you will not be able to find the SQLite DB on the document's folder.
You can copy your database, click finder and write this address(/Users/administrator/Library/Application Support/iPhone Simulator/6.1/Applications/) in finder click ok.
You will get documentary path.
Open your project document file and paste your database....