I have the following code and it is implemented based on a tutorial on youtube,, but I changed some of it to meet my purpose which is inserting items hard coded .. the database is created and also the table but I found that addItems is not adding.
I don't think that there is wrong with addItemToTable method,
I think it is a logical error in the first part listed here, but can not find it .Any help will be great
thanks in advance
these are the items:
table_ok = YES;
if (table_ok) {
if (!db_open_status) {
[self openDBWithSQLName:dataBaseName];
NSLog(#"DB opened");
}
NSMutableDictionary *objectColsVals = [[NSMutableDictionary alloc]init];
NSString *this_id = #"12";
NSString *this_name = #"and";
NSString *this_email = #"123#hotmail.com";
NSString *this_password = #"aa11111";
NSString *this_role = #"Marketing";
[objectColsVals setValue:this_id forKey:[my_columns_names objectAtIndex:0]];
[objectColsVals setValue:this_name forKey:[my_columns_names objectAtIndex:1]];
[objectColsVals setValue:this_email forKey:[my_columns_names objectAtIndex:2]];
[objectColsVals setValue:this_password forKey:[my_columns_names objectAtIndex:3]];
[objectColsVals setValue:this_role forKey:[my_columns_names objectAtIndex:4]];
if ([[objectColsVals allKeys] count] > 0) {
if ([self addItemToTable:tableName WithColumnValues:objectColsVals]) {
NSLog(#"inserted");
[self closeDB];
}
}
This the method:
-(BOOL)addItemToTable:(NSString *)usetable WithColumnValues:(NSDictionary *)valueObject{
BOOL has_beenAdded = NO;
NSString *mycolumns = #"";
NSString *myvalues = #"";
//loop through all the value keys
for (int r=0; r<[[valueObject allKeys] count]; r++) {
NSString *this_keyname = [[valueObject allKeys]objectAtIndex:r];
mycolumns = [mycolumns stringByAppendingString:this_keyname];
NSString *thisval = [NSString stringWithFormat:#"'%#'",[valueObject objectForKey:this_keyname]];
myvalues = [myvalues stringByAppendingString:thisval];
//add commas to seperate the col and val lists before last item
if (r<(([[valueObject allKeys] count])-1)) {
mycolumns = [mycolumns stringByAppendingString:#","];
myvalues = [myvalues stringByAppendingString:#","];
}
}
NSString *myinsert = [NSString stringWithFormat:#"INSERT INTO %# (%#) VALUES(%#)",usetable,mycolumns,myvalues];
char *err;
if (sqlite3_exec(estate_db, [myinsert UTF8String], NULL, NULL, &err) != SQLITE_OK) {
sqlite3_close(estate_db);
}else{
has_beenAdded = YES;
}
return has_beenAdded;
}
In terms of what you've got here, it's hard to say where the problem is. Nothing seems obviously wrong. At a very minimum, one should:
examine what the resulting INSERT statement that you built programmatically, to make sure there isn't some subtle issue that eludes a cursory examination of the code;
if any sqlite3_xxx() calls fail (notably if the sqlite3_exec returns anything besides SQLITE_OK), then log the error message (either the err variable, or by calling sqlite3_errmsg()); if you don't look at these error messages you're just flying blind; and
run the app on the simulator and then open the simulator's copy of the database on your Mac (in the ~/Library/Application Support/iPhone Simulator directory; if the ~/Library folder is hidden, unhide it by running the with chflags -nohidden ~/Library command in the Terminal command line tool) and examine the contents of the database directly. Verify column names, table names, etc.
Again, it's unclear where the problem is, but it likely rests in something simple like some confusion when opening the database or creating the table in question. Until we confirm error messages and the actual SQL, it's hard to say. It could be anything from an attempt to open the readonly copy of the database in the bundle to erroneously calling sqlite3_open and unwittingly creating a new blank database. You really should update the question and share the code that creates the database (or copies it from the bundle), as well as doing some of the diagnostic steps outlined above.
Having said this, I really discourage you from adding values into your SQL with stringWithFormat. The dynamic building of the SQL is fine, but you really should not the use of stringWithFormat to insert the values into the SQL, itself. Given that you're quoting the text values with single quotes, what if the person's last name was O'Brian? Or if you changed your routine to use double quotes, what if the person's name was Dwayne "The Rock" Johnson? The current code may fail if the string delimiter occurs in the data value. Worse, you technically expose yourself to SQL injection attacks.
What you should generally do is to use ? placeholders. For example, consider a dictionary defined as follows:
NSDictionary *dataToInsert = #{#"name" : #"Jack",
#"id" : #37,
#"password" : #"feefifofum",
#"role" : [NSNull null],
#"email" : #"jack#magicbeans.fairyland.com",
#"hourly_wage" : #12.85};
What you want to do is to build a SQL statement that looks like the following:
INSERT INTO test (name,id,password,role,email,hourly_wage) VALUES (?,?,?,?,?,?)
You then want to bind the values to those ? placeholders using the sqlite3_bind_xxx() functions.
So, you can create and prepare that SQL statement (building an array of values and an array of placeholders) like so:
NSArray *keys = [dataToInsert allKeys];
NSMutableArray *values = [NSMutableArray arrayWithCapacity:[keys count]];
NSMutableArray *placeholders = [NSMutableArray arrayWithCapacity:[keys count]];
// build array of values and array of question mark placeholders
for (NSString *key in keys) {
[values addObject:[dataToInsert objectForKey:key]];
[placeholders addObject:#"?"];
}
// use the `keys` and `placeholders` arrays to build the SQL
NSString *insertSql = [NSString stringWithFormat:#"INSERT INTO %# (%#) VALUES (%#)",
tableName,
[keys componentsJoinedByString:#","],
[placeholders componentsJoinedByString:#","]];
if (sqlite3_prepare_v2(db, [insertSql UTF8String], -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"prepare failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
return;
}
// statement is prepared, but we still have to bind the values...
You can then bind the values with something like the following. This is doing dynamic checking of the class of the objects in the values array (and if it is a NSNumber, look at the objCType to determine the type of number):
// now use the `values` array to bind values to the ? placeholders
[values enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
int rc = SQLITE_ERROR;
if ([obj isKindOfClass:[NSString class]])
rc = sqlite3_bind_text(statement, idx + 1, [obj UTF8String], -1, SQLITE_TRANSIENT);
else if ([obj isKindOfClass:[NSNull class]])
rc = sqlite3_bind_null(statement, idx + 1);
else if ([obj isKindOfClass:[NSNumber class]]) {
const char *objCType = [obj objCType];
if (strcmp(objCType, #encode(int)) == 0 || strcmp(objCType, #encode(unsigned int)) == 0 || strcmp(objCType, #encode(short)) == 0 || strcmp(objCType, #encode(unsigned short)) == 0 || strcmp(objCType, #encode(char)) == 0 || strcmp(objCType, #encode(unsigned char)) == 0)
rc = sqlite3_bind_int(statement, idx + 1, [obj integerValue]);
else if (strcmp(objCType, #encode(long)) == 0 || strcmp(objCType, #encode(unsigned long)) == 0 || strcmp(objCType, #encode(long long)) == 0 || strcmp(objCType, #encode(unsigned long long)) == 0)
rc = sqlite3_bind_int64(statement, idx + 1, [obj longLongValue]);
else if (strcmp(objCType, #encode(float)) == 0 || strcmp(objCType, #encode(double)) == 0)
rc = sqlite3_bind_double(statement, idx + 1, [obj doubleValue]);
else {
NSLog(#"column %d is %# but has unknown numeric type %s; will use `description`", idx + 1, obj, objCType);
rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);
}
}
else
rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);
if (rc != SQLITE_OK)
{
NSLog(#"bind %d failed: %s", idx + 1, sqlite3_errmsg(db));
sqlite3_finalize(statement);
sqlite3_close(db);
return;
}
}];
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"step failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
return;
}
sqlite3_finalize(statement);
Related
I have a tableview, its header is stored in a mutablearray, the array looks like
(2005 fall, 2005 spring, 2007 summer...)
When I output the tableview, I want the header in time ascending displayed.
2005 spring
2005 fall
2007 summer
I used the code here:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
[self.sectionKeys sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSString *key = [self.sectionKeys objectAtIndex:section];
return key;
}
It works fine with year. However, fall comes before spring and summer because of alphabetreason , what to do to fix it please?
Use a custom comparator to get a custom sort order:
NSMutableArray *array = [#[ #"2005 fall", #"2005 spring", #"2007 summer" ] mutableCopy];
NSArray *seasons = #[ #"spring", #"summer", #"fall", #"winter" ];
[array sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
NSArray *parts1 = [str1 componentsSeparatedByString:#" "];
NSArray *parts2 = [str1 componentsSeparatedByString:#" "];
NSString *year1 = parts1[0];
NSString *year2 = parts2[0];
NSComparisonResult yearRes = [year1 compare:year2 options:NSNumericSearch];
if (yearRes == NSOrderedSame) {
NSString *season1 = parts1[1];
NSString *season2 = parts2[1];
NSUInteger index1 = [seasons indexOfObject:season1];
NSUInteger index2 = [seasons indexOfObject:season2];
if (index1 < index2) {
return NSOrderedAscending;
} else if (index1 > index2) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
} else {
return yearRes;
}
}];
Note - I might have the NSOrderedAscending and NSOrderedDescending backwards. Swap them if the sort of the seasons in the same year come out in the reverse order.
You need a lookup mechanism to define the ordering of the seasons
NSArray *seasons = #[#"spring", #"summer", #"fall", #"winter"];
NSArray *strings = #[#"2005 fall",#"2007 spring", #"2005 spring", #"2007 winter", #"2005 winter"];
strings = [strings sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
NSArray *string1Comps = [obj1 componentsSeparatedByString:#" "];
NSArray *string2Comps = [obj2 componentsSeparatedByString:#" "];
NSComparisonResult compareYearResult = [#([string1Comps[0] integerValue]) compare:#([string2Comps[0] integerValue]) ];
if (compareYearResult == NSOrderedSame) {
return [#([seasons indexOfObject:string1Comps[1]]) compare:#([seasons indexOfObject:string2Comps[1]])];
}
return compareYearResult;
}];
result
(
2005 spring,
2005 fall,
2005 winter,
2007 spring,
2007 winter
)
Another look up mechanism could be a block
NSNumber* (^lookUpSeason)(NSString *) = ^(NSString *seasonname){
static NSArray *seasons;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
seasons = #[#"spring", #"summer", #"fall", #"winter"];
});
return #([seasons indexOfObject:seasonname]);
};
This might look a bit cumbersome at first, but increases readability when used.
return [#([seasons indexOfObject:string1Comps[1]]) compare:#([seasons indexOfObject:string2Comps[1]])];
becomes
return [lookUpSeason(string1Comps[1]) compare:lookUpSeason(string2Comps[1])];
in both cases you could also give the lookup code into the comparator block, this will give you the opportunity to remove the same comparator with the lookup in other places.
like:
NSArray *strings = #[#"2005 fall", #"2007 spring", #"2005 spring", #"2007 winter", #"2005 winter", #"2005 summer", #"2000 hhh"];
NSComparisonResult (^yearAndSeasonComparator)(id,id) = ^NSComparisonResult(NSString *obj1, NSString *obj2) {
NSNumber* (^lookUpSeason)(NSString *) = ^(NSString *seasonname){
static NSArray *seasons;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
seasons = #[#"spring", #"summer", #"fall", #"winter"];
});
return #([seasons indexOfObject:seasonname]);
};
NSArray *string1Comps = [obj1 componentsSeparatedByString:#" "];
NSArray *string2Comps = [obj2 componentsSeparatedByString:#" "];
NSComparisonResult compareYearResult = [#([string1Comps[0] integerValue]) compare:#([string2Comps[0] integerValue]) ];
if (compareYearResult == NSOrderedSame) {
return [lookUpSeason(string1Comps[1]) compare:lookUpSeason(string2Comps[1])];
}
return compareYearResult;
};
strings = [strings sortedArrayUsingComparator:yearAndSeasonComparator];
The block assigned to yearAndSeasonComparator could now be reused in other places that would sort similar strings.
So you have an array with section keys. But the sections are not in order of the array, they need to be sorted. You will notice that cellForRowAtIndexPath: needs the exact same information. So sorting in this place is wrong.
What I do to handle this: I have a property "unsortedSectionKeys" and a property "sortedSectionKeys". sortedSectionKeys has a getter that checks for nil and stores a sorted copy of unsortedSectionKeys if it is nil. And whenever unsortedSectionKeys changes, you just set sortedSectionKeys to nil. (That solves at least some problems).
For your sorting, you need to write proper code. Use (void)sortUsingComparator:(NSComparator)cmptr to sort a mutable, or - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr to get a sorted copy of an array.
Example:
[self.sectionKeys sortArrayUsingComparator:^NSComparisonResult(NSString* obj1, NSString* obj2) {
NSInteger year1 = obj1.integerValue;
NSInteger year2 = obj2.integerValue;
if (year1 < year2) return NSOrderedAscending;
if (year1 > year2) return NSOrderedDescending;
NSInteger season1 = 0;
if ([obj1 rangeOfString:#"spring" options:NSCaseInsensitiveSearch].location != NSNotFound)
season1 = 1;
if ([obj1 rangeOfString:#"summer" options:NSCaseInsensitiveSearch].location != NSNotFound)
season1 = 2;
if ([obj1 rangeOfString:#"fall" options:NSCaseInsensitiveSearch].location != NSNotFound)
season1 = 3;
if ([obj1 rangeOfString:#"winter" options:NSCaseInsensitiveSearch].location != NSNotFound)
season1 = 4;
NSInteger season2 = 0;
if ([obj2 rangeOfString:#"spring" options:NSCaseInsensitiveSearch].location != NSNotFound)
season2 = 1;
if ([obj2 rangeOfString:#"summer" options:NSCaseInsensitiveSearch].location != NSNotFound)
season2 = 2;
if ([obj2 rangeOfString:#"fall" options:NSCaseInsensitiveSearch].location != NSNotFound)
season2 = 3;
if ([obj2 rangeOfString:#"winter" options:NSCaseInsensitiveSearch].location != NSNotFound)
season2 = 4;
if (season1 < season2) return NSOrderedAscending;
if (season1 > season2) return NSOrderedDescending;
return NSOrderedSame;
}];
Your decision if winter is the first or last season in the year, since usually it's December to February.
I need to check a timestamp in an SQLite database table against a timestamp I have as a variable. When I run a select statement against my table I appear to be missing the first record.
lastMod = dict[#"LastMod"];
[self openDB];
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT LastMod FROM %# order by Lastmod desc", newTableName];
const char *sqlQueryChars = [sqlQuery cStringUsingEncoding:[NSString defaultCStringEncoding]];
sqlite3_stmt *statementChk;
sqlite3_prepare_v2(congressDB, sqlQueryChars, -1, &statementChk, NULL);
if (sqlite3_step(statementChk) == SQLITE_ERROR){
NSAssert1(0, #"Error: %s", sqlite3_errmsg(congressDB));
sqlite3_close(congressDB);
} else {
if (sqlite3_step(statementChk) == SQLITE_ROW) {
NSString *resultString = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statementChk, 0)];
NSNumberFormatter *resultNumberFormatter = [[NSNumberFormatter alloc] init];
[resultNumberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *resultNumber = [resultNumberFormatter numberFromString:resultString];
if (resultNumber){
if ([lastMod integerValue] > [resultNumber integerValue]) {
needsUpdating = #"YES";
} else {
needsUpdating = #"NO";
}
}
} else {
needsUpdating = #"YES"; // No rows found, table is empty and needs filling
}
The newest records in the database are:
1390393860
1390385996
1390385681
...
The one I want and am expecting is the top one - 1390393860, the biggest number (which is the newest record). But I am getting the second one 1390385996. If I change the query to:
NSString *sqlQuery = [NSString stringWithFormat:#"SELECT LastMod FROM %# order by Lastmod desc limit 1", newTableName];
The program says no rows were returned. Testing this query directly on the database returns the correct row. What's going on?
Thanks.
It's cos you call sqlite_step() twice:
if (sqlite3_step(statementChk) == SQLITE_ERROR){
NSAssert1(0, #"Error: %s", sqlite3_errmsg(congressDB));
sqlite3_close(congressDB);
} else {
if (sqlite3_step(statementChk) == SQLITE_ROW) {
When you should be using the last call only:
if (sqlite3_step(statementChk) == SQLITE_ROW) {
(you want to fetch the next row and react only if there is an error).
I have a database, and i do a S ELECT statement on SQL for 10 random rows.
It's for Iphone App, so Objective C.
How could i get back the information after the statement ?
...
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement");
}
while (sqlite3_step(sql1Statement)==SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
}
For now, i get back info just for the first row. How could i get back the info (id) for the others rows.
I would like something like that
numeroqdonnee2 =
numeroqdonnee3 =
numeroqdonnee4 =
...
Many thanks
You could add the results to some array:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *results = [NSMutableArray array];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
[results addObject:#(numeroqdonnee)];
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
// now do whatever you want with this results array
NSLog(#"results = %#", results);
// or
[results enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
NSLog(#"numeroqdonnee%d = %d", idx, [obj integerValue]);
}];
Or you could just log the results directly:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
NSLog(#"numeroqdonnee = %d", numeroqdonnee);
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
Note, I'd suggest you log the sqlite3_errmsg if you have any problems (otherwise you're just flying blind). I've also added the sqlite3_finalize (which your original code sample may have had, but I just wanted to make sure).
Following is a code segment which I learnt in a lynda iOS training course based on iOS 4 (without ARC).
I was going to implement the same code in my Xcode 4.2 which has ARC turned on with the iOS 5 SDK. It gave me this error:
error: Semantic Issue: Sending '__strong id *' to parameter of type '__unsafe_unretained id **' changes retain/release properties of pointer"
- (NSNumber *) insertRow:(NSDictionary *) record {
// NSLog(#"%s", __FUNCTION__);
int dictSize = [record count];
// the values array is used as the argument list for bindSQL
id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys
id values[dictSize];
[record getObjects:values andKeys:keys]; // convenient for the C array
// construct the query
NSMutableArray * placeHoldersArray = [NSMutableArray arrayWithCapacity:dictSize];
for (int i = 0; i < dictSize; i++) // array of ? markers for placeholders in query
[placeHoldersArray addObject: [NSString stringWithString:#"?"]];
NSString * query = [NSString stringWithFormat:#"insert into %# (%#) values (%#)",
tableName,
[[record allKeys] componentsJoinedByString:#","],
[placeHoldersArray componentsJoinedByString:#","]];
[self bindSQL:[query UTF8String] arguments:(va_list)values];
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
return [self lastInsertId];
} else {
NSLog(#"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
}
** The real case came with the following segment of this whole function.**
int dictSize = [record count];
// the values array is used as the argument list for bindSQL
id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys
id values[dictSize];
[record getObjects:values andKeys:keys]; // convenient for the C array
How can I resolve this?
There's a lot wrong with that function. The fix for your compiler error is simply to declare keys and values with the __unsafe_unretained ownership qualifier:
__unsafe_unretained id keys[dictSize]; // not used, just a side-effect of getObjects:andKeys
__unsafe_unretained id values[dictSize];
However, the cast (va_list)values is undefined behavior. Don't do it.
Since you're not using the keys array at all, it would be better to do something like this:
- (NSNumber *) insertRow:(NSDictionary *) record {
int count = [record count];
NSMutableArray * placeHoldersArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
[placeHoldersArray addObject: #"?"];
}
NSString * query = [NSString stringWithFormat:#"insert into %# (%#) values (%#)",
tableName,
[[record allKeys] componentsJoinedByString:#","],
[placeHoldersArray componentsJoinedByString:#","]];
[self bindSQL:[query UTF8String] arguments:[record allValues]];
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
return [self lastInsertId];
} else {
NSLog(#"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
}
And then modify bindSQL:arguments: to take an NSArray * instead of a va_list. If you need help doing that, show us the source code of bindSQL:arguments:.
(However, I don't think it's documented that allKeys and allValues return parallel arrays…)
Edited for const char pointer:
- (NSNumber *) insertRow:(NSDictionary *) record {
NSArray * placeHoldersArray = [NSArray array];
const char * cStrings[record.count];
NSArray * keys = [record allKeys];
NSUInteger i=0;
for (id key in keys) {
id object = [record objectForKey:key];
if ([object isKindOfClass:[NSString class]])
cStrings[i++] = [(NSString*)object UTF8String];
else
#throw [NSException exceptionWithName:NSInvalidArgumentException reason:#"Expected NSString" userInfo:nil];
placeHoldersArray = [placeHoldersArray arrayByAddingObject: #"?"];
}
NSString * query = [NSString stringWithFormat:#"insert into %# (%#) values (%#)",
tableName,
[keys componentsJoinedByString:#","],
[placeHoldersArray componentsJoinedByString:#","]];
[self bindSQL:[query UTF8String] arguments:(va_list)cStrings];
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
return [self lastInsertId];
} else {
NSLog(#"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
return [NSNumber numberWithInt:0];
}
}
In the update method, you should understand that a va_list is just a group of any number of arguments. It is assumed that the method that is called knows how to determine the type. An example of this is with NSLog. It uses the format string to determine the type of an unknown number of variables, which are passed in as a va_list, i.e. comma separated values. Because you only have one value, just pass in the value. I am sketchy on whether sqlite wants a c string or if it will take an integer in this case.
I'm developing an iPhone app. I've got a function that reads data from a sqlite database and puts the results into an array. Everything works fine. Here is part of the function that fills the array:
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aVar1 = [NSString stringWithUTF8String(char*)sqlite3_column_text(compiledStatement, 0)];
NSString *aVar2 = [NSString stringWithUTF8String(char*)sqlite3_column_text(compiledStatement, 1)];
NSArray *anArray = [NSArray arrayWithObjects:aVar1,aVar2,nil];
[returnArray addObject:anArray]
[anArray release];
}
//return the array
I want to make this function more generic so that it takes a sql statement string as a parameter, and returns a mutablearray of arrays, no matter how many columns are in the result set.
Is there a way to do this? The solution doesn't have to include arrays -- could be any collection object. I'm just looking for a way to make the function re-usable for other queries to the same database.
Couldn't you just do something like:
int numCols = sqlite3_column_count(compiledStatement);
NSMutableArray *result = [NSMutableArray array];
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < numCols; i++) {
[array addObject:
[NSString stringWithUTF8String:
(char *)sqlite3_column_text(compiledStatement, i)]];
}
[result addObject:array];
}
+(NSArray *)executeQueryAndReturnArray:(NSString *)query {
sqlite3_stmt *statement = nil;
const char *sql = [query UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"[SQLITE] Error when preparing query!");
} else {
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(statement) == SQLITE_ROW) {
NSMutableArray *row = [NSMutableArray array];
for (int i = 0; i < sqlite3_column_count(statement); i++) {
int colType = sqlite3_column_type(statement, i);
id value;
if (colType == SQLITE_TEXT) {`enter code here`
const unsigned char *col = sqlite3_column_text(statement, i);
value = [NSString stringWithFormat:#"%s", col];
} else if (colType == SQLITE_INTEGER) {
int col = sqlite3_column_int(statement, i);
value = [NSNumber numberWithInt:col];
} else if (colType == SQLITE_FLOAT) {
double col = sqlite3_column_double(statement, i);
value = [NSNumber numberWithDouble:col];
} else if (colType == SQLITE_NULL) {
value = [NSNull null];
} else {
NSLog(#"[SQLITE] UNKNOWN DATATYPE");
}
[row addObject:value];
}
[result addObject:row];
}
return result;
}
return nil;
}