I'm having some difficulty with my sqlite prepare statement. I get an error saying my table does not exist, although I've checked in multiple places for it, and it does exist, so I'm confuzzled.
The file is in the correct iPhone Simulator Application folder
The file is added to my project and viewable in the project navigator
It is also in my build phases- Copy Bundle Resources area.
I've cleaned and started running again.
The database exists and running my sql statement gets me just the
results I expected.
- (NSMutableArray *) getMyWorkout{
NSMutableArray *workoutArray = [[NSMutableArray alloc] init];
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"IOSDB.sqlite"];
NSLog(#"Db path is %#",dbPath);
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success) {
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK)){
sqlite3_close(db);
NSLog(#"Failed to open database with message '%s'.", sqlite3_errmsg(db));
}
const char *sql = "SELECT Id, type, difficulty, duration, description FROM workoutTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK){
NSLog(#"%s Prepare failure '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
} //...
When I run it, I get the file path and the following error
2013-02-01 18:07:08.060 TriShake[9251:c07] -[MyWorkoutList getMyWorkout] Prepare failure 'no such table: workoutTbl' (1)
I've checked out these other questions, but have been unable to find a solution
Sqlite Prepare Failed: no such table<tablename>
Sqlite3 gives "no such table" error on iPhone
I understand sqlite3_open() creates an empty database for you if the database path does not exist, but i know it exists, so frustration ensues. Any help or guidance you could give me would be much appreciated.
In terms of your immediate problem, it's going to be something simple.
You say you've "cleaned and built again", but have you actually removed the old app from your simulator? Either remove the app manually, or, easier, just reset the simulator entirely by choosing "reset content and settings" from the "iOS Simulator" menu. Sometimes Xcode is not good about knowing what files to copy over (particularly in a case like this where your running it on the device may be changing the timestamp of the file in the simulator's bundle!)
Run the app again.
If the app doesn't work as expected, open up the database in the simulator folder from the Mac and check out the database to make sure the table is there and precisely as you expected it to be. So navigate to the app, open the bundle (you may have to choose the "show package contents" option), confirm the existence of the database, but just as importantly, open it up this particular copy of the database in your favorite Mac sqlite3 tool of choice and confirm the existence of the table there.
Let us know what you find. Again, it's got to be something simple such as:
Perhaps the process of rebuilding the app was not reinstalling everything; I've occasionally had problems where Xcode elected to not re-copy something during the install on my simulator;
Perhaps your database in your project was accidentally put in a subdirectory, worse, you might have two copies sitting in different directories;
Perhaps the database in your Xcode project is missing (or has a typo or (esp in the case of the device) has incorrect filename capitalization) in the name of the table or file;
Etc.
For a lot of these errors, you won't notice the problem until you completely reset the simulator itself. There are a million little things it could be, but hopefully completely resetting the simulator and starting over will help you find the issue. It's always something simple when it comes to these sorts of issues.
Some other minor observations:
You probably should not be opening databases from the bundle. Programmatically copy it from the bundle to the Documents folder, and open the database from there. I know it seems unnecessary, but it's important for a myriad of reasons (if db changes during operation of the app, if db accidentally gets created on you, don't let Xcode get confused about things that changed (even if only file timestamps) in the bundle changing behind Xcode's back, etc.)
You should, if you need the database to be there, use sqlite3_open_v2, using either SQLITE_OPEN_READWRITE or SQLITE_OPEN_READONLY for flags (but do not include SQLITE_OPEN_CREATE). It causes headaches to ever give sqlite a chance to create a blank database for you, or otherwise modify it, so never give it an opportunity to so.
I have encounter the same problem as yours. If the IOS can not find the designated database file, defaultly it will create one for you instead of throwing an error. So you must open the database file IOS created for you which is blank so it off course contain the table you expected.
what I deal with it :
1 you have to bundle the resource file named *.sqlite3 into your project
2 Then You have to use [NSBundle mainBundle] pathFordirectory...... function to search your proper database file.
then you can open the database file you expected and can operate it properly
Best regards,
Not enough rep to comment on Jack's post, but that helped me.
In my case, I had mistyped my path for resource extension:
// Wrong
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"productList"
ofType:#"sqlite3"];
// Should have been (added db ext)
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"productList"
ofType:#"db"];
I would always get past the:
if (sqlite3_open([sqLiteDb UTF8String], &_database) == SQLITE_OK))
because it was automatically creating a db file for me.
Related
just wondering if someone else came across this.
I got this piece of code that used to work brilliant in previous xcode versions.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:#"mydb.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"mydb" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
[self addSkipBackupAttributeToItemAtURL:storeUrl];
NSError *error;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
Typical reasons for an error here include:
* The persistent store is not accessible
* The schema for the persistent store is incompatible with current managed object model
Check the error message to determine what the actual problem was.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
I would expect the following from this:
an empty "mydb" to be created from scratch if there is no "mydb.sqlite" in my bundle.
if a "mydb.sqlite" exists in my main bundle then i would expect it to be copied in the specified diretory.
if the "mydb.sqlite" is not compatible with my xcdatamodel the app must crash.
however this works only with already created db's previously.
If for example i try to put a random db named "mydb.sqlite" to my bundle and delete the original one then,
the app doesnt crash!!! a blank db is created and the new db is ignored.
This is completely wrong as it goes against my code.
In addition if I add back the original db nothing happens and the app just creates a blank db.
(and yes I do clean my project, delete the sim app, and even delete the build folder before any change occurs!)
any ideas??
There is a difference between your code and your expectation as you've stated it.
Here is your expectation:
an empty "mydb" to be created from scratch if there is no "mydb.sqlite" in my bundle.
if a "mydb.sqlite" exists in my main bundle then i would expect it to be copied in the specified directory.
if the "mydb.sqlite" is not compatible with my xcdatamodel the app must crash.
Here is what your code does:
Look for mydb.sqlite in the Documents directory
If mydb.sqlite does not exist, copy it from the main bundle (if it exists in the main bundle)
Create a new persistent store at ~/Documents/mydb.sqlite
The main problem I think you're experiencing is because of Step 3. addPersistentStoreWithType:... creates a new store at the URL provided. You instead need to requery the existence of the file (to check that there is now a file existing which may have been copied from the MainBundle) and if it exists, then use [persistentStoreCoordinator persistentStoreForURL:storeURL] instead of creating a new persistent store.
Once you have fixed this, the other problems should be more easily traceable. I suggest adding many more NSLog's in to your code, so you can see whether the code is following the execution path you expect. E.g. log each new object you create so you can easily see if any of them are nil. When you copy your bundle db, add an NSError pointer rather than NULL, and then if the error is non-nil, log it.
the answer to the question/ observation is simple.
downgrade to 4.2.1
I have restored my xcode to 4.2.1 (thank God for Time Machine) and now ALL is as expected.
I will file a bug report to Apple later on today.
Check that your default db is actually being included in the bundle - i.e. it is checked as included in the project, and is in the copy files build phase. I rearranged a project and an SQLite file, whilst bring copied into the project structure, was not included in my project - this would cause the behaviour you are seeing.
I'm new to Xcode and trying to learning myself, my problem may be naive but I really need helps.
I'm learning creating an ebook by learning from a finished project with source code.
It works perfectly about recording bookmark through sqlite3.
But when I create a new project with different name and path, built pharse of libsqlite3.dylib correctly and copied almost everything of the code, the else statement is called and crashes when I'm trying to visit bookmark window.
Here's some question I asked myself,
did I miss anything, or failed on changing names or paths? answered: no.
as the one runs perfectly and the other one with almost same code crashes, what's the most possible mistake? answered: the very few differences from these two project,etc path, name, content in file.
Then I changed the database file name of the good project, and it becomes crashing.
I couldn't believe, because I heard that sqlite3_open([dbPath UTF8String], &dataBase); will create file if it doesn't exist, why won't it just create a new file with the new name after it was told to access the new file?
Second test I did is manually changing the db file path of the new project to the db file path of the original one. like the path is no more [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; but it is #"/Users/~(path of the original one)~/Documents/archive" and omg it works.
But of course I can't leave it like that cause that path means nothing on a real iPhone and it still depends the db file of the original one.
At least I know the path really matters and I seems really missing some thing like db file in my new project.
But the thing is I didn't find any db file in the path of the original project, it seems exist and important, but doesn't show in finder.
I even tried to copy the file to my new path programmaticlly, like calling [self createEditableCopyOfDatabaseIfNeeded]; with the new and the old path.
That's all I tried so far.
The strange thing is it somehow record font and color after an unknown attempt, but still crashes on going to bookmark window.
That's all I tried in three days. It may help you answer the question or at least know how I was upset after thousands attempts.
Waiting for help and thank you very much!
const char *createSQL = "CREATE TABLE IF NOT EXISTS BOOKMARK(pos REAL PRIMARY KEY,\fontsize REAL,\bookpath TEXT)";
newly added: As the program automatically issue the crash when the database is not prepared, the error meg, as someone said, is useless. All I need is maybe making sqlite3_prepare(dataBase, [querySQL UTF8String], -1, &statement, nil) == SQLITE_OK not false..
The SQL you're using to create the table in the new DB appears to be messed up. Try removing the \'s
Try this instead:
const char *createSQL = "CREATE TABLE IF NOT EXISTS BOOKMARK(pos REAL PRIMARY KEY, fontsize REAL, bookpath TEXT)";
Additionally you should take a look at what sqlite3_prepare actually returns. The SQLite documentation gives a list of error codes to check against. An example of logging the error code:
int ret = sqlite3_prepare(dataBase, [querySQL UTF8String], -1, &statement, nil);
if (ret != SQLITE_OK) {
NSLog(#"Error calling sqlite3_prepare: %d", ret);
}
Why don't you print out the value written to errorMsg? That will at least tell you why you're not creating the database.
NSLog(#"%#",[NSString stringWithUTF8String:errorMsg]);
I'm having a problem with some sqlite code for an iPhone program in Xcode. I was opening my database like this:
int result = sqlite3_open("stealtown.db", &database);
Which is how they had it in a book I was looking at while I type the program. But then, that way of opening a database it only works when you run in simulator, not on device. So I finally figure out I need to do this:
NSString *file = [[NSBundle mainBundle] pathForResource:#"stealtown" ofType:#"db"];
int result = sqlite3_open([file UTF8String], &database);
And that works on device, EXCEPT one thing: Each time you launch the program, it starts as if you had never created the database, and when you stick an entry in the table, it's the ONLY entry in that table.
When I used the first code on the simulator, I could open my program 6 times, each time adding 1 entry to a table, and at the end, I had 6 entries in that table. With the second code, I do exact same thing but each time there is only 1 entry in that table. Am I explaining this okay, I hope so, it's hard sometimes for me.
Anyone maybe know why this would be?
On the device, the app bundle is not writable. Therefore, your database file cannot be changed. You have to create the database in your app's Documents directory on first launch. Or, if your app comes with prepopulated data, you have to copy the database file from your app bundle to the Documents directory first and then open the copy.
So I have been working on this project for a short while now. I have no problems with reading data from the DB, and formatting it into UITableViews and what not. But now I am wanting also to write to the DB as well. The problem is I keep getting a "Database is Locked" error from sqlite. After messing around with the original version I had the face-palm moment by realizing my database was in the bundle and therefore not writable. So I relocated the DB to the Apps Documents folder, which is writeable. But now I still get the same "Database is Locked" sql error. I only open and close the DB when necessary. And as far as I can tell, I don't leave it open anywhere. Below is the code where I am wanting to do updates. Any thoughts?
- (BOOL) loanBookTo:(NSString *)newborrower{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"books.sqlite"];
if([[NSFileManager defaultManager] fileExistsAtPath:path]){
NSLog(#"File Exists at: %#", path);
}
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
NSString *mySQL = #"UPDATE BOOKS SET LOANED = 1, BORROWER = \"<BORROWER>\" where ISBN = \"<ISBN>\"";
mySQL = [mySQL stringByReplacingOccurrencesOfString:#"<ISBN>" withString:self.isbn];
mySQL = [mySQL stringByReplacingOccurrencesOfString:#"<BORROWER>" withString:newborrower];
const char *sql = [mySQL UTF8String];
char* errmsg;
sqlite3_exec(database, sql, NULL, NULL, &errmsg);
// Q. Database is locked. Why?
// A. Because it is in the Bundle and is ReadOnly.
// Need to write a copy to the Doc folder.
// Database is Locked error gets spit out here.
printf(errmsg);
sqlite3_close(database);
}
return NO;
}
Open the database once at the start of your app, then close it in applicationWillTerminate of the AppDelegate.
After every exec you will want to do a reset to clear the connection state.
Take a look at the SQLiteBooks sample app, it is coded this way.
How do you move the DB to the documents folder from the bundle? You need to check that it is there, and if not copy it. I get the feeling that either you have copied it some other way, but retained a read-only attribute, or more likely, you are still referencing the original in the bundle.
For details see
Where would you place your SQLite database file in an iPhone app?
or as joshperry says, the SQLiteBooks sample has all the code you need.
How are you getting to loanBookTo? If the database is open and then you call loanBookTo, it may not throw an error of the open, however the database is holding the state from where you came.
Also, at times, the database retains the locked state upon closing and exiting the application, so you could be 'inheriting' a locked state from your previous failures. Deleting the app from the simulator should give you a clean copy.
I created a sqlite3 database, created tables and insert some data. I can retrieve using select query by using terminal application.
But when i add this database to my iPhone application resources and try to access data programatically I get error as "no such table: table name"
Why does this happen?
sqlite3_open() creates an empty database for you if the database path does not exist. So, it is possible that the path you gave it does not lead you to the intended file. With an empty database, you get "no such table" a lot.
The DB file is probably not reachable by the iPhone. Try creating the DB from the iPhone app itself.
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES
);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [
documentsDirectory stringByAppendingPathComponent:#"yourdbname.sql"
];
// Open the database. The database was prepared outside the application.
if (sqlite3_open([path UTF8String], &db_handle) != SQLITE_OK) {
// Even though the open failed,
// call close to properly clean up resources.
sqlite3_close(db_handle);
NSAssert1(0,
#"Failed to open database with message '%s'.",
sqlite3_errmsg(db_handle)
);
}
And the perform your query again. Also, it helps if you check for potential errors on every step of your DB access code.
I had the same error with sqlite3X C++ wrapper.
I use this library in my IE-plugin.
The problem was in unavailability .sqlite base.
Current directory for IE(not plugin) is "C:\Documents and Settings\UserName\desktop\".
When I put my base in this location - problem was solved.