Does SQLite support "datareader"? - objective-c

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);

Related

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.

Return strings in sequence from NSMutableArray [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I have a NSMutableArray with comma separated strings for an objective-c iphone application (iOS SDK 6.0). I need a simple function that, when triggered, returns a string, one at a time, from string 0 onwards. To give you some context, a user would click a button, and for every click a new string is returned, in order, from the Array. It's a list of previously saved "favourite quotes". The string is displayed in a UITextView.
Ideally, I would also have a function for reversing, i.e. going backwards in the array from the current position.
This might be pretty basic, but I seem to only be able to find more advanced implementations that I'm unable to translate into this looping backwards and forwards in an Array of strings.
EDIT: Current code for this function below. I need to add the part where one string at a time is returned from the array (allRows) and displayed in a textview
- (IBAction)nextQoute:(id)sender {
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_qoutesDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT qoutesSaved FROM qoutes"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_qoutesDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
NSMutableArray *allRows = [[[NSMutableArray alloc] init] autorelease];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *qouteField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSString *str = [NSString stringWithFormat:#"%#", qouteField];
[allRows addObject:str];
[qouteField release];
}
Very thankful for help!
You mention that you are new to Objective C, and based on how I read what you're describing, I wonder if you are making this a lot more complicated than it needs to be. Items in an array (that is, NSArray, or NSMutableArray) aren't "seperated" by anything - different items in an array are accessed by their order in the array, called an index. So in your code, when you build allRows, each str that you put into it gets put into its own index, and to get it back out of the array, you just use that index.
For example, let's pretend your quotes you are pulling from your database are:
"Here's looking at you kid"
"I'll be back"
"It's a trap"
If they are put into the array in that order, and you want to put "It's a trap" into a textfield (called myTextField), you just write
myTextField.text = [allRows objectAtIndex:2];
In the end, that means what you probably want to do for your app is keep a counter you pass to objectAtIndex. When the user clicks the forward button, increase the count. When they click the back button, decrease the count. Then, call the code I put above, except instead of sending the value 2, send your counter variable. As mentioned in the comments to H2CO3s answer, make sure you put some validation in there to prevent your count from going beyond the limits of your array, or you'll get a nasty crash. If this is indeed what you are trying to do and you are still confused, I can add some more code.
I'm not too familiar with accessing a sqlite database the way you do here (I use CoreData, so the calls are very different), so perhaps I am completely mistaken about what you are trying to do here - but this seems like a very simple task that is being very overcomplicated.
You can just keep track of the string index in an instance variable (or if you don't have an object to work with because you write a class method or a function and not an instance method, then you can use a static local variable too).
#interface Foo: NSObject {
NSInteger index;
NSArray *strings;
}
// ...
- (NSString *)nextString
{
return index < strings.count ? strings[index++] : nil;
}
- (NSString *)previousString
{
return index > 0 ? strings[--index] : nil;
}
You can fetch the whole data in database.And store it one array
declare these two objects.
#interface YourClassName: NSObject {
NSInteger stringIndex;
NSArray *quoteFieldDataArr;
}
NSMutableArr *quoteFieldDataArr=[self fetchDataFromDataBase];
-(NSMutableDictionary *)fetchDataFromDataBase
{
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_qoutesDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT qoutesSaved FROM qoutes"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_qoutesDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
NSMutableDictionary *allRows = [[[NSMutableDictionary alloc] init] autorelease];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *qouteField = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
NSString *str = [NSString stringWithFormat:#"%#", qouteField];
[allRows addObject:str];
[qouteField release];
return nil;
}
// Load Next String
- (NSString *)loadNextString
{
return stringIndex < quoteFieldDataArr.count ? quoteFieldDataArr[stringIndex++] : nil;
}
- (NSString *)loadpreviousString
{
return stringIndex > 0 ? quoteFieldDataArr[--stringIndex] : nil;
}

core data fetch data order by id

I'm trying to get allways the same order for my fetch data, because all the time I request, he get another sorted array so I'm trying to order by ID but I couldn't.
My situation:
I have 2 models, FORM and FORMCOMPONENT
1 FORM have many FORMCOMPONENT
when I do this:
Form *form = ...;
[form formComponents]; //I get all the components but each time I run I get with a differente order
How I suppose order this if I don't have any field for ORDER? On android I did this with the ID.. that's why I don't have any field with this proposal.
I tryed to order in a array with sortWithComparator but I can't do this because I'm not allowed to get the number ID (only the number, not the entire string he give me with [obj objectID] ). This is funny because when I use sqlite3 for see the database only have the number there.
Another way I thought about is get the FORMCOMPONENTs direct with a new request, not by FORM one ([form formComponents]), like this:
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"FormComponent" inManagedObjectContext:[appDelegate managedObjectContext]];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
//SOME PREDICATE WITH THE FORM
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray *arrayComps = (NSMutableArray*) [[appDelegate managedObjectContext] executeFetchRequest:request error:&error];
so, anyone have an idea? Thanks.
--- EDIT ---
PS: I don't want to avoid the problem creating another field for order. If I don't have any other solution so I will do this.
You can sort by objectID by using a sort descriptor with self as the sort key. The underlying SQL will have ORDER BY t0.Z_PK, Z_PK being the internal ID of the record (as found as the last component of the object's NSManagedObjectID).
Core data is no database.
Internally Core Data uses PK, of course. But Core Data is no database, it is a system to model a graph. Consequently no "ID information" is published. Looking for an ID is anti-conceptional.
If your data does not have a "natural order", there should be nothing wrong in getting a random order. If it has a natural order, simply use sort descriptors.
If you need an order "creation date" simply add a property creationDate and set it in -awakeFromInsert. (BTW: AFAIK it is an urban legend, that PK always raises. A PK can be something else than autoincrement.)
Core Data doesn't expose any kind of SQL-style numeric ID that you could use for sorting. If you don't want to add an attribute to sort on, the easiest thing might be to configure this relationship as being ordered. Then you get an NSOrderedSet for the relationship, and you can keep individual instances in whatever order you want. Make the relationship ordered in the Core Data model editor, and use mutableOrderedSetValueForKey: when you want to add a new instance to the relationship.
The recommended way it to add a field for the ID/sort order you want to give to your components.
Alternatively you could make the relationship ordered by checking the checkbox for that in the CoreData editor. This has some more overhead, but is the only option if components need to appear in multiple lists.
Meddling with SQLite directly isn't a smart approach.
Thanks for the help. I found what I'm looking for (with this I can order for example)
I need to get PK direct from sql, if anyone needs do this, here is the code:
Just don't forget what our friend Tom said: "That's not how Core Data works"
-(NSMutableArray*) readDatabase {
NSLog(#"READ");
sqlite3 *database;
NSMutableArray *components = [[NSMutableArray alloc] init];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
NSString *databasePath = [documentsDir stringByAppendingPathComponent:#"Model.sqlite"];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"SQLITE_OK");
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from zformcomponent";
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
int aPK = sqlite3_column_int(compiledStatement, 0);
//NSString *aDescription = [NSString stringWithUTF8String: (char *)sqlite3_column_text(compiledStatement, 2)];
//NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
NSLog(#"PK: %d",aPK);
//Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];
//[animals addObject:animal];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return components;
}

Getting number of rows from SQLite C interface in Objective-C

I am new to objective-C and iphone apps.
I am accessing SQLite and have 3 rows in my table "coffee". I used the following way to grab sth out from the table, however, only then 2nd and 3rd rows are being pulled out {the 1st row is always missed}. Is that due to the logic in my while loop by checking while sqlite3_step(selectstmt) returns SQLITE_ROW is wrong? Here is the code:
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select coffeeID, coffeeName from coffee";
sqlite3_stmt *selectstmt;
NSLog(#"sqlite_prepare_v2 returns: %i", sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL));
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
NSLog(#"sqlite3_step returns: %i", sqlite3_step(selectstmt));
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Coffee *coffeeObj = [[Coffee alloc] initWithPrimaryKey:primaryKey];
coffeeObj.coffeeName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
NSLog(#"this is the coffee name: %#", coffeeObj.coffeeName);
coffeeObj.isDirty = NO;
[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
On the other hand, is there any convenient way for me to check the number of rows returen in a query directly from the C interface of SQLite?
Many thanks.
You could use the query SELECT COUNT(*) FROM coffee to tell you how many rows there are.
And also, save yourself some headaches and use a SQLite wrapper.
Are the 2 sqlite3_step() calls meant to be executed here?
NSLog(#"sqlite3_step returns: %i", sqlite3_step(selectstmt));
while(sqlite3_step(selectstmt) == SQLITE_ROW {
BTW: there a parenthesis missing in the while line. Do not rewrite your code for SO. Copy/Paste it to avoid copying errors (pasting errors are much more rare)

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?