Populating array from select query in iOS

cocoa-touch, ios, ios7, nsmutablearray, sqlite

Solution

You are never instantiating `columnNames`. Thus, your attempt to add column names to that array will not succeed. To remedy this, when you declare it, you want to instantiate the mutable array object, too:

NSMutableArray *columnNames = [NSMutableArray array];

Unrelated to this problem, when you're done retrieving the column names, before you prepare your second statement, don't forget to release the memory associated with the first prepared statement:

sqlite3_finalize(statement);

Finally, when you're done retrieving the second prepared SQL statement, rather than calling `sqlite3_reset`, you want to call `sqlite3_finalize` again for that second prepared statement. The `sqlite3_reset` is used to reset a statement when you want to bind new values to `?` placeholders in a statement, which is not applicable here, so no `sqlite3_reset` is needed. But if you don't call `sqlite3_finalize`, you're not releasing the memory associated with the prepared statement.

By the way, if you wanted to dynamically retrieve the column names and column types (without having to do `PRAGMA table_info`), you could do something like:

int rc;

if ((rc = sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, NULL)) != SQLITE_OK) {
    NSLog(@"select failed %d: %s", rc, sqlite3_errmsg(database));
}

NSMutableArray *returnArray = [NSMutableArray array];
NSInteger columnCount = sqlite3_column_count(statement);

id value;

while ((rc = sqlite3_step(statement)) == SQLITE_ROW) {
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

    for (NSInteger i = 0; i < columnCount; i++) {
        NSString *columnName   = [NSString stringWithUTF8String:sqlite3_column_name(statement, i)];
        switch (sqlite3_column_type(statement, i)) {
            case SQLITE_NULL:
                value = [NSNull null];
                break;
            case SQLITE_TEXT:
                value = [NSString stringWithUTF8String:(const char *)sqlite3_column_text(statement, i)];
                break;
            case SQLITE_INTEGER:
                value = @(sqlite3_column_int64(statement, i));
                break;
            case SQLITE_FLOAT:
                value = @(sqlite3_column_double(statement, i));
                break;
            case SQLITE_BLOB:
            {
                NSInteger length  = sqlite3_column_bytes(statement, i);
                const void *bytes = sqlite3_column_blob(statement, i);
                value = [NSData dataWithBytes:bytes length:length];
                break;
            }
            default:
                NSLog(@"unknown column type");
                value = [NSNull null];
                break;
        }
        dictionary[columnName] = value;
    }

    [returnArray addObject:dictionary];
}

if (rc != SQLITE_DONE) {
    NSLog(@"error returning results %d %s", rc, sqlite3_errmsg(database));
}

sqlite3_finalize(statement);

Problem

So I'm fairly new to iOS development and I'm having problems with my select function. I made a function that should take in a select query and the table name and return an array of results where each array entry is a dictionary with a row of results. Somehow my query for column names is deleting my `columnNames` variable and returning crazy results. I'm just trying to figure out an easy way to store, access, manipulate query results Here is the function that converts results into array: ``` -(NSMutableArray *)selectQuery:(NSString*)query table:(NSString*)table { NSMutableArray *returnArray = [NSMutableArray new]; if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) { NSMutableArray *columnNames; NSString *tableQuery = [NSString stringWithFormat:@"PRAGMA table_info('%@')", table]; if (sqlite3_prepare_v2(database, [tableQuery UTF8String], -1, &statement, nil) == SQLITE_OK) { while (sqlite3_step(statement) == SQLITE_ROW) { [columnNames addObject:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, 1)]]; } } else { NSLog(@"Error preparing table query:"); NSLog(tableQuery); } if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil) == SQLITE_OK) { while(sqlite3_step(statement)==SQLITE_ROW) { NSMutableDictionary *temp= [NSMutableDictionary new]; for (int i = 0; i < [columnNames count]; i++) { [temp setObject:[NSString stringWithUTF8String:(const char*)sqlite3_column_text(statement, i)] forKey:columnNames[i]]; } if (temp != nil) { [returnArray addObject:temp]; temp = nil; } } sqlite3_reset(statement); sqlite3_close(database); } else { NSLog(@"Error preparing select statement with query:"); NSLog(query); } } else { NSLog(@"Could not open database"); } return returnArray; } ``` and heres the call to it ``` NSMutableArray *queryResults = [dbInstance selectQuery:[NSString stringWithFormat:@"SELECT gallons, mileage FROM fillups WHERE carId = \"%d\" ORDER BY date asc", carId] table:@"fillups"]; ```

Original source