sqlite3_prepare_v2 is getting SQLITE_ERROR - objective-c

I have been at this for hours and MUST get this working! It is holding up an iPhone app release... My first time using SQLite. I have followed all the advice and yet my sqlite3_prepare_v2 call gets a SQLITE_ERROR (1) every time!
Here is my code from my controller:
NSString *query = #"SELECT * FROM QandA ORDER BY random() LIMIT 1";
// const char *sqlStatement = "SELECT * FROM QandA ORDER BY random() LIMIT 1";
sqlite3_stmt *compiledStatement;
// sqlite3_stmt *statement;
int prepareStatus = sqlite3_prepare_v2(database, [query UTF8String],
-1, &compiledStatement, NULL);
if (prepareStatus == SQLITE_OK) {...
You'll note that I've tried using a "char *" also to no avail (among other attempts). My database opens fine with:
databaseName = #"Facts.sqlite";
// Get the path to the documents directory and append the databaseName
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
NSLog(#"databasePath = %#", databasePath);
int dbOpenStatus = sqlite3_open_v2([databasePath UTF8String], &database, SQLITE_OPEN_READWRITE, NULL);
From my controller interface:
NSString *databaseName;
NSString *databasePath;
I've checked in the debugger and everything looks good, but the prepare statement fails. I don't know how to log the statement it is trying to compile... I assume/hope it is just what my SELECT says.
Can anyone help? I'm desperate. Mark

Found the answer here. I had to use this instead for the path to the DB file:
[[NSBundle mainBundle]pathForResource:#"Facts"extension:#"sqlite"];
This gave a slightly different path (one extra directory) - once I used that it worked! Hope this helps someone else... I spent many hours on this.

Please note that the above is slightly inaccurate, the ofType should be used not extension, and the ofType obviously needs to match your file extension.
If your DB is named "mysqldatabase.sql" change your path URL to:
databasePath = [[NSBundle mainBundle]pathForResource:#"mysqldatabase" ofType:#"sql"];

The code seems to be fine, might be a silly question but have you created the QandA table inside of the database?

Related

How to get the path to the current workspace/screen's wallpaper on OSX?

Since AppKit version 10.7, NSWorkspace.desktopImageForScreen may return a path to a folder instead of a file's URL which is currently the wallpaper. This folder is the the place from where the wallpapers will be sequentially picked up for display. (Search for setDesktopImageURL in the release notes).
In case the user has set the desktop image to change randomly every thirty minutes or so, is there any way of determining what are the currently active wallpapers per screen in OSX?
Update: Based no the answer by #l'L'l, I created a small Mac OSX app to conveniently find the currently active Wallpapers: https://github.com/musically-ut/whichbg
On OS X 10.10 there is a SQLite 3.x database named desktoppicture.db. This db file stores info such as the current desktop picture, directory, space, interval, etc. when a timed random desktop picture transition happens or when there's any change to System Preferences > Desktop:
Objective-C
// Get Current Desktop Picture
- (IBAction)getDesktopPicture:(id)sender {
[self getCurrentDesktop];
}
-(void)getCurrentDesktop {
NSMutableArray *sqliteData = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *appSup = [paths firstObject];
NSString *dbPath = [appSup stringByAppendingPathComponent:#"Dock/desktoppicture.db"];
sqlite3 *database;
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT * FROM data";
sqlite3_stmt *sel;
if(sqlite3_prepare_v2(database, sql, -1, &sel, NULL) == SQLITE_OK) {
while(sqlite3_step(sel) == SQLITE_ROW) {
NSString *data = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sel, 0)];
[sqliteData addObject:data];
}
}
}
NSUInteger cnt = [sqliteData count] - 1;
NSLog(#"Desktop Picture: %#", sqliteData[cnt]);
NSLog(#"%#",sqliteData);
sqlite3_close(database);
}
Result:
2015-06-23 14:36:04.470 CurrentDesktop[72591:87862519] Desktop
Picture: Poppies.jpg 2015-06-23 14:36:04.470
CurrentDesktop[72591:87862519] (
"60.0",
1,
"Poppies.jpg" )
There are quite a few different other ways you can get the data from this file (eg. NSTask, Bash, AppleScript, etc. This is my preferred solution since it's native mac code; it's simple enough to make portable for something else though.

Objective C: SQLite where-statement wont work when running another method first

So basically I have an app that will provide tasks based on selected project. Both projects and tasks are stored in a SQLite database.
To get the current project id I compare the selected project (_selectedProject) to my database, to get the ID. This is done in my getSelectedProjectId method. However, when running this method in the getTasks method, the Where-statement wont work at all. If I don't run the getSelectedProjectId method first, it works just fine. Am I forgetting to release something? Or is it something else? Any ideas?
I'm pretty new to both SQLite and Objective C, so this may not be a complex issue. I have made sure the getSelectedProjectId method returns the correct project ID. I have also made sure the query that is run in the getTasks method is correct, and when running it through my terminal it returns a number of rows. In the app it returns nothing, provided I'm running the getSelectedProjectId somewhere in that method first.
This is the method that fetches the tasks:
- (void)getTasks
{
[self openDB];
sqlite3_stmt *statement;
int projectId = [self getSelectedProjectId];
NSString *query = [NSString stringWithFormat:#"SELECT * FROM tasks WHERE project_id=%i", projectId];
const char *query_statement = [query UTF8String];
sqlite3_prepare_v2(_contactDB, query_statement, -1, &statement, NULL);
while (sqlite3_step(statement) == SQLITE_ROW)
{
// I add the task title to my array of tasks here.
}
sqlite3_finalize(statement);
sqlite3_close(_contactDB);
}
And this is the method that gets the correct project id from the database:
- (int)getSelectedProjectId
{
sqlite3_stmt *statement;
NSString *query = [[NSString alloc]
initWithFormat:#"SELECT id FROM projects WHERE title=\"%#\" LIMIT 0,1",
_selectedProject];
int rowId = 0;
const char *query_statement = [query UTF8String];
[self openDB];
sqlite3_prepare_v2(_contactDB, query_statement, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_ROW)
{
rowId = sqlite3_column_int(statement, 0);
}
sqlite3_finalize(statement);
sqlite3_close(_contactDB);
return rowId;
}
The problem occured because I closed the DB connection in my getSelectedProjectId-method. I'm now leaving my DB open instead, works like a charm.

Weird behavior of fopen on ios

I am trying to create a file by fopen and then write it, but weird things happened.
When I plug in the iphone to the usb port. Everything works fine. A file is created at the tmp directory or the document directory as expected.
When I plug off the device and do the same thing, the file did not appear. I was wondering why.
I use fopen to create the file. In my case, I should do this to create and then write the file. The call is fopen(pcm_output, "wb+");
You need to use this call.
char const *path = [fileManager fileSystemRepresentationWithPath:url.path];
From the docs...
fileSystemRepresentationWithPath:
- (const char *)fileSystemRepresentationWithPath:(NSString *)path
iOS (2.0 and later)
Returns a C-string representation of a given path that properly encodes Unicode strings for use by the file system.
path: A string object containing a path to a file.
A C-string representation of path that properly encodes Unicode strings for use by the file system.
You're probably writing outside of the sandbox, can you post the path?
Just as a test try to turn on iTunes Sharing (this should have no effect, it's just a test) for your app.
EDIT:
After testing I discovered that you have to use:
NSString *docsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [docsPath stringByAppendingPathComponent:[NSString stringWithCString:pcm_output encoding:NSUTF8StringEncoding]];
fopen([filePath UTF8String], "wb+");
Instead of just:
fopen([filePath UTF8String], "wb+");
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory,
NSUserDomainMask, YES
);
NSString* docDir = [paths objectAtIndex:0];
_tempLogPath = [docDir stringByAppendingPathComponent: #"Aisound5_CBLog.log"];
_tempPcmPath = [docDir stringByAppendingPathComponent: #"OutPcm.pcm"];
_tempWavPath = [docDir stringByAppendingPathComponent: #"OutWav.wav"];
tts_resource = [[bundle pathForResource:#"Resource_dev" ofType:#"irf"] UTF8String];
tts_log = [_tempLogPath UTF8String];
pcm_output = [_tempPcmPath UTF8String];
wav_output = [_tempWavPath UTF8String];
The original code is this, in which tts_resource tts_log pcm_output and wav_output are defined in a .h file and used in a .c file with fopen.
I had tried in your way to init the const string with the explicit const char* style, but the problem remains the same.

Does SQLite support "datareader"?

I'm trying to use a datareader in SQLite, but am unable to find anything in the docs I have ("Using SQLite" by Kreibich).
Can someone tell me if it's supported and where I can find some examples?
Yes, you just need to get yourself System.Data.SQLite.
It comes in two variants, one that has SQLite built-in, and another which requires that you also ship a separate native sqlite DLL.
the sqlite api has a concept which is logically equivalent to the .net reader. which means, you issue a query and then iterate read data as needed. that keeps memory low as your not pulling the complete result set into memory.
first of all, take a look at other wrappers like fmdb.
here's the equivalent using the c api inside of iPhone. You prepare the statement by passing the sql query (sqlite parses under the cover), then you call step which is the equivalent to the .net reader read method. The you read columns just like the .net data reader. Note that this example prepares and finalizes (cleans up). A more efficient approach is to save the prepared statement and then call reset to avoid having sqlite parse the query over and over.
// prep statement
sqlite3_stmt *statement;
NSString *querySQL = #"SELECT id, name FROM contacts";
NSLog(#"query: %#", querySQL);
const char *query_stmt = [querySQL UTF8String];
// preparing a query compiles the query so it can be re-used.
sqlite3_prepare_v2(_contactDb, query_stmt, -1, &statement, NULL);
// process result
while (sqlite3_step(statement) == SQLITE_ROW)
{
int idx = 0;
Contact *contact = [[Contact alloc] init];
NSNumber *idField = [NSNumber numberWithLongLong:sqlite3_column_int64(statement, idx++)];
[contact setId:idField];
NSString *nameField = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, idx)];
[contact setName:nameField];
NSLog(#"id: %#", [contact id]);
NSLog(#"name: %#", [contact name]);
[nameField release];
[contactsList addObject:contact];
[contact release];
}
sqlite3_finalize(statement);

Sqlite3 INSERT INTO Question × 377

I am creating an exercise app that will record the weight used and the number of "reps" the user did in 4 "Sets" per day over a period of 7 days so the user may view their progress.
I have built the database table named FIELDS with 2 columns ROW and FIELD_DATA and I can use the code below to load the data into the db. But the code has a sql statement that says,
INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA)VALUES (%d, '%#');
When I change the statment to:
INSERT INTO FIELDS (ROW, FIELD_DATA)VALUES (%d, '%#');
Nothing happens. That is no data is recorded in the db.
Below is the code:
#define kFilname #"StData.sqlite3"
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kFilname];
}
-(IBAction)saveData:(id)sender;
{
for (int i = 1; i <= 8; i++)
{
NSString *fieldName = [[NSString alloc]initWithFormat:#"field%d", i];
UITextField *field = [self valueForKey:fieldName];
[fieldName release];
NSString *insert = [[NSString alloc] initWithFormat:
#"INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA)
VALUES (%d, '%#');",i, field.text];
// sqlite3_stmt *stmt;
char *errorMsg;
if (sqlite3_exec (database, [insert UTF8String],
NULL, NULL, &errorMsg) != SQLITE_OK)
{
// NSAssert1(0, #"Error updating table: %s", errorMsg);
sqlite3_free(errorMsg);
}
}
sqlite3_close(database);
}
So how do I modify the code to do what I want? It seemed like a simple sql statement change at first but obviously there must be more. I am new to Objective-C and iPhone programming.
I am not new to using sql statements as I have been creating web apps in ASP for a number of years.
Any help will be greatly appreciated, this is driving me nuts!
Suggestions:
write an insert statement with hardcoded values to see if the insert works
your filename has no path. does it assume the current directory when executed? what directory is it running from?
write a message to the screen if possible to see what the values you're getting are. Are they correct?