iOS SQLite SELECT with UTF 8 characters - ios

I've been reading all questions related to SQlite encoding with no success, so I'll try to ask about my specific case.
I have an iOS app with a SQLite database prepopulated.
This SQLite has been converted from a MySQL database.
Both the SQLite and MySQL databases have UTF8 enconding (the conversion process set the default sqlite enconding to UTF8)
If I open my sqlite database with Firefox SQlite Manager I can read all the content without problems, and all UTF8 chars are shown properly, so I guess everything was converted correctly.
But, when I try to perfom a select with UTF8 chars inside, the query fails.
This is the method where I perform the queries:
- (NSArray *)performQuery:(NSString *)query
{
static sqlite3 *db;
sqlite3_stmt *resultado;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"mydatabase.sqlite"];
const char *dbpath = [writableDBPath UTF8String];
if (sqlite3_open(dbpath, &db) == SQLITE_OK)
{
printf("%s\n", [query UTF8String]);
int codigo = sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL);
if (codigo == SQLITE_OK)
{
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(resultado) == SQLITE_ROW)
{
NSMutableArray *row = [NSMutableArray array];
for (int i = 0; i < sqlite3_column_count(resultado); i++)
{
int colType = sqlite3_column_type(resultado, i);
id value;
if (colType == SQLITE_TEXT)
{
value = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(resultado, i)];
if ([value isEqualToString:#"(null)"] || [value isEqualToString:#""] || (value == nil) || [value isKindOfClass:[NSNull class]])
{
value = #"";
}
}
else if (colType == SQLITE_INTEGER)
{
int col = sqlite3_column_int(resultado, i);
value = [NSNumber numberWithInt:col];
}
else if (colType == SQLITE_FLOAT)
{
double col = sqlite3_column_double(resultado, i);
value = [NSNumber numberWithDouble:col];
}
else if (colType == SQLITE_NULL)
{
value = [NSNull null];
}
else
{
NSLog(#"ERROR: DataBase - performQuery - Campo desconocido");
}
[row addObject:value];
}
[result addObject:row];
row = nil;
}
sqlite3_finalize(resultado);
sqlite3_close(db);
resultado = nil;
return result;
}
else
{
NSLog(#"ERROR [%d](%s): %#", codigo, sqlite3_errmsg(db), query);
}
}
else
{
NSLog(#"ERROR: No se pudo abrir la base de datos");
}
sqlite3_close(db);
db = nil;
resultado = nil;
return nil;
}
And this is the method where I call performQuery:
- (int) getIdForItem:(NSString *)item
{
NSString *query = [NSString stringWithFormat:#"SELECT item_id FROM table_items WHERE item_name=\"%#\"", item];
NSLog(#"QUERY:\n%#", query);
NSArray *answer = [self performQuery:query];
if([answer count] > 0)
return [[[answer objectAtIndex:0] objectAtIndex:0] intValue];
return -1;
}
If I make this call:
[self getIdForItem:#"Camión"];
This is the output:
QUERY:
SELECT item_id FROM table_items WHERE item_name="Camión"
SELECT item_id FROM table_items WHERE item_name="Camión"
ERROR [1](unrecognized token: ""Cami√≥n"): SELECT item_id FROM table_items WHERE item_name="Camión"
This error is produced in the method:
sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL)
So, Why is Camión interpreted as Cami√≥n in sqlite_prepare_v2? Both the query and the sqlite database are using UTF8, so I guess I'm missing something...

Well, the rmaddy's solution worked perfectly for me. What I've done is:
PerformQuery is almost the same, it receives a new argument:
- (NSArray *)performQuery:(NSString *)query withArgument:(NSString *)arg
Just after the sqlite3_prepare_v2 I've made the bind:
int codigo = sqlite3_prepare_v2(db,[query UTF8String],[query length],&resultado,NULL);
if (codigo == SQLITE_OK)
{
sqlite3_bind_text(resultado, 1, [arg UTF8String], -1, SQLITE_TRANSIENT);
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(resultado) == SQLITE_ROW)
And finally, I call this method this way:
- (int) getIdForItem:(NSString *)item
{
NSString *query = #"SELECT item_id FROM table_items WHERE item_name=?1";
NSLog(#"QUERY:\n%#", query);
NSArray *answer = [self performQuery:query];
if([answer count] > 0)
return [[[answer objectAtIndex:0] objectAtIndex:0] intValue];
return -1;
}

Related

How to use Lock/Unlock?

Here is the code that using SQLite.
This function will be call from some threads.
I don't know well, following code is correct one or not.
There are lock/unlock code.
Please advice me whether I used lock/unlock correctly.
- (NSMutableArray*) Query:(NSString *)query_str forColumn:(NSString*)mycolumn
{
sqlite3_stmt *statement;
const char *query_stmt = [query_str UTF8String];
[dbLock lock];
if (sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL) != SQLITE_OK) {
[dbLock unlock];
return nil;
}
NSMutableArray *queryResult = [NSMutableArray array];
while (sqlite3_step(statement) == SQLITE_ROW) {
NSMutableDictionary * dict = [[NSMutableDictionary alloc] init];
int num_of_fields = sqlite3_column_count(statement);
for (int i = 0; i < num_of_fields; i++) {
NSString * col_name = [[NSString alloc] initWithUTF8String:sqlite3_column_name(statement, i)];
const char * value = (const char *)sqlite3_column_text(statement, i);
NSString * col_text;
if (value)
col_text = [[NSString alloc] initWithUTF8String:value];
else
col_text = #"";
if (col_name && col_text) {
if ([col_text isEqualToString:#"--"] == NO && [col_text isEqualToString:#""] == NO)
[dict setObject:col_text forKey:col_name];
}
}
if (mycolumn == nil) {
[queryResult addObject:dict];
} else {
NSString *tmp = [dict objectForKey:mycolumn];
if (tmp != nil) {
[queryResult addObject:tmp];
}
}
}
sqlite3_finalize(statement);
[dbLock unlock];
return queryResult;
}
The locking is around all the database access functions, so as long as all the other database access code also does similar locking, the database is protected.
The query_str access is outside the lock, so it is not protected against concurrent modifications. Whether that could happen in your program is something only you can decide.

inserting multiple tables data in sqlite database

I have created multiple tables in my database. And now I want insert data into those tables. How to insert multiple tables data can anyone help regarding this.
I have written this code for creating 1 table:
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO ATRG (id, name, language,imgurl) VALUES ( \"%#\",\"%#\",\"%#\",\"%#\")", ID, name, lang,imgUrl];
const char *insert_stmt = [insertSQL UTF8String]; sqlite3_prepare_v2(_globalDataBase, insert_stmt, -1, &statement, NULL); if (sqlite3_step(statement) == SQLITE_DONE)
{
NSLog(#"Record Inserted");
} else { NSLog(#"Failed to Insert Record");
}
Try this I hope it would be helpful!! This is mine code for insert data
#import "Sqlitedatabase.h"
#implementation Sqlitedatabase
+(NSString* )getDatabasePath
{
NSString *docsDir;
NSArray *dirPaths;
sqlite3 *DB;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
NSString *databasePath = [docsDir stringByAppendingPathComponent:#"myUser.db"];
NSFileManager *filemgr = [[NSFileManager alloc]init];
if ([filemgr fileExistsAtPath:databasePath]==NO) {
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath,&DB)==SQLITE_OK) {
char *errorMessage;
const char *sql_statement = "CREATE TABLE IF NOT EXISTS users(ID INTEGER PRIMARY KEY AUTOINCREMENT,FIRSTNAME TEXT,LASTNAME TEXT,EMAILID TEXT,PASSWORD TEXT,BIRTHDATE DATE)";
if (sqlite3_exec(DB,sql_statement,NULL,NULL,&errorMessage)!=SQLITE_OK) {
NSLog(#"Failed to create the table");
}
sqlite3_close(DB);
}
else{
NSLog(#"Failded to open/create the table");
}
}
NSLog(#"database path=%#",databasePath);
return databasePath;
}
+(NSString*)encodedString:(const unsigned char *)ch
{
NSString *retStr;
if(ch == nil)
retStr = #"";
else
retStr = [NSString stringWithCString:(char*)ch encoding:NSUTF8StringEncoding];
return retStr;
}
+(BOOL)executeScalarQuery:(NSString*)str{
NSLog(#"executeScalarQuery is called =%#",str);
sqlite3_stmt *statement= nil;
sqlite3 *database;
BOOL fRet = NO;
NSString *strPath = [self getDatabasePath];
if (sqlite3_open([strPath UTF8String],&database) == SQLITE_OK) {
if (sqlite3_prepare_v2(database, [str UTF8String], -1, &statement, NULL) == SQLITE_OK) {
if (sqlite3_step(statement) == SQLITE_DONE)
fRet =YES;
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return fRet;
}
+(NSMutableArray *)executeQuery:(NSString*)str{
sqlite3_stmt *statement= nil; // fetch data from table
sqlite3 *database;
NSString *strPath = [self getDatabasePath];
NSMutableArray *allDataArray = [[NSMutableArray alloc] init];
if (sqlite3_open([strPath UTF8String],&database) == SQLITE_OK) {
if (sqlite3_prepare_v2(database, [str UTF8String], -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSInteger i = 0;
NSInteger iColumnCount = sqlite3_column_count(statement);
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
while (i< iColumnCount) {
NSString *str = [self encodedString:(const unsigned char*)sqlite3_column_text(statement, (int)i)];
NSString *strFieldName = [self encodedString:(const unsigned char*)sqlite3_column_name(statement, (int)i)];
[dict setObject:str forKey:strFieldName];
i++;
}
[allDataArray addObject:dict];
}
}
sqlite3_finalize(statement);
}
sqlite3_close(database);
return allDataArray;
}
#end
And called that method where you want to use!!
NSString *insertSql = [NSString stringWithFormat:#"INSERT INTO users(firstname,lastname,emailid,password,birthdate) VALUES ('%#','%#','%#','%#','%#')",_firstNameTextField.text,_lastNameTextField.text,_emailTextField.text,_passwordTextField.text,_BirthdayTextField.text];
if ([Sqlitedatabase executeScalarQuery:insertSql]==YES)
{
[self showUIalertWithMessage:#"Registration succesfully created"];
}else{
NSLog(#"Data not inserted successfully");
}
And If you want to fetch data from table then you can do this!!
NSString *insertSql = [NSString stringWithFormat:#"select emailid,password from users where emailid ='%#' and password = '%#'",[_usernameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]],[_passwordTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
NSMutableArray *data =[Sqlitedatabase executeQuery:insertSql];
NSLog(#"Fetch data from database is=%#",data);
Multiple Execute Query!!
NSString *insertSql = [NSString stringWithFormat:#"INSERT INTO users (firstname,lastname,emailid,password,birthdate) VALUES ('%#','%#','%#','%#','%#')",_firstNameTextField.text,_lastNameTextField.text,_emailTextField.text,_passwordTextField.text,_BirthdayTextField.text];
NSString *insertSql1 = [NSString stringWithFormat:#"INSERT INTO contact (firstname,lastname,emailid,password,birthdate) VALUES ('%#','%#','%#','%#','%#')",_firstNameTextField.text,_lastNameTextField.text,_emailTextField.text,_passwordTextField.text,_BirthdayTextField.text];
NSMutableArray * array = [[NSMutableArray alloc]initWithObjects:insertSql,insertSql1,nil];
for (int i=0; i<array.count; i++)
{
[Sqlitedatabase executeScalarQuery:[array objectAtIndex:i]];
}
See this for your issue:
Insert multiple tables in same database in sqlite
or if you want
Multi-table INSERT using one SQL statement in AIR SQLite
then use this:
http://probertson.com/articles/2009/11/30/multi-table-insert-one-statement-air-sqlite/

saving json data into sqlite database in iOS

My question is how to insert a fetched JSON array data into sqlite database.
I have fetched JSON data which is an array of dictionaries.
My code to save JSON result looks like this:
-(BOOL) saveApiResults: (NSString *)tableName : (NSArray *)data
{
BOOL saveSuccess = NO;
#try {
const char *dbPath = [databasePath UTF8String];
if(sqlite3_open(dbPath,&database)==SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
//pass an array containing json dictionary to below line
NSDictionary *rowData=[data objectAtIndex:0];
NSArray *keyArray = [rowData allKeys];
NSLog(#"key array %#",keyArray);
NSString *insertSQL=#"INSERT INTO ";
insertSQL=[insertSQL stringByAppendingString:#"moodsdata"];
insertSQL=[insertSQL stringByAppendingString:#" VALUES("];
for(int j=0;j<[keyArray count];j++)
{
insertSQL=[insertSQL stringByAppendingString:#"?"];
if(j<[keyArray count]-1)
insertSQL=[insertSQL stringByAppendingString:#","];
}
insertSQL=[insertSQL stringByAppendingString:#");"];
NSLog(#"query : %# ",insertSQL);
const char *sqlstatement = [insertSQL UTF8String];
sqlite3_stmt *compiledstatement;
if(sqlite3_prepare_v2(database,sqlstatement , -1, &compiledstatement, NULL)==SQLITE_OK) {
for (NSUInteger i = 0; i < [data count]; i++) {
NSDictionary *rowData=[data objectAtIndex:0];
for(int j=0;j<[keyArray count];j++) {
NSString *val = #"";
NSString *value=(NSString *)[rowData objectForKey:[keyArray objectAtIndex:j]];
if((value != nil) && (![value isEqual:[NSNull null]]))
val=[NSString stringWithFormat:#"%#",value];
NSLog(#"values %#",val);
sqlite3_bind_text(compiledstatement,j+1,[val UTF8String], -1, SQLITE_TRANSIENT);
}
if(sqlite3_step(compiledstatement) != SQLITE_DONE) {
NSLog(#"ERROR");
}
sqlite3_clear_bindings(compiledstatement);
sqlite3_reset(compiledstatement);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
saveSuccess = YES;
NSLog(#"RESULTS SAVED SUCCESSFULLY!");
} else {
NSLog(#"StatemenT FAILED (%s)", sqlite3_errmsg(database));
}
sqlite3_finalize(compiledstatement);
} else {
NSLog(#"Statement FAILED (%s)", sqlite3_errmsg(database));
}
}
#catch (NSException *exception) {
NSLog(#"NSException : %#",exception.description);
}
#finally {
sqlite3_close(database);
}
return saveSuccess;
}
My question is when I try to pass the JSON array to this method it only saves the value for first array object . i.e. only first dictionary values get saved. Please tell me what I am doing wrong.
You primary issue is that you mistakenly access data[0] instead of data[i] in your loop that does the actual inserting.
But there are lots of other little issues with this code. Here's your code all cleaned up:
-(BOOL) saveApiResults:(NSString *)tableName data:(NSArray *)data
{
BOOL saveSuccess = NO;
const char *dbPath = [databasePath UTF8String];
if (sqlite3_open(dbPath,&database) == SQLITE_OK) {
sqlite3_exec(database, "BEGIN", 0, 0, 0);
//pass an array containing json dictionary to below line
NSDictionary *rowData = data[0];
NSArray *keyArray = [rowData allKeys];
NSLog(#"key array %#",keyArray);
NSMutableString *insertSQL = #"INSERT INTO moods data VALUES(";
for (NSInteger j = 0; j < [keyArray count]; j++)
{
if (j) {
[insertSQL appendString:#","];
}
[insertSQL appendString:#"?"];
}
[insertSQL appendString:#");"];
NSLog(#"query : %# ",insertSQL);
const char *sqlstatement = [insertSQL UTF8String];
sqlite3_stmt *compiledstatement;
if (sqlite3_prepare_v2(database, sqlstatement, -1, &compiledstatement, NULL) == SQLITE_OK) {
for (NSDictionary *rowData in data) {
for (NSInteger j = 0; j < [keyArray count]; j++) {
NSString *val = #"";
NSString *value = rowData[keyArray[j];
if (value && ![value isEqual:[NSNull null]]) {
val = [NSString stringWithFormat:#"%#",value];
}
NSLog(#"values %#",val);
sqlite3_bind_text(compiled statement, j + 1, [val UTF8String], -1, SQLITE_TRANSIENT);
}
if (sqlite3_step(compiledstatement) != SQLITE_DONE) {
NSLog(#"ERROR");
}
sqlite3_reset(compiledstatement);
}
sqlite3_exec(database, "COMMIT", 0, 0, 0);
sqlite3_finalize(compiledstatement);
saveSuccess = YES;
NSLog(#"RESULTS SAVED SUCCESSFULLY!");
} else {
NSLog(#"StatemenTtFAILED (%s)", sqlite3_errmsg(database));
}
sqlite3_close(database);
} else {
NSLog(#"Statement FAILED (%s)", sqlite3_errmsg(database));
}
return saveSuccess;
}
There is no need for try/catch. Just do proper error checking.
Use NSMutableString to build up a string piece by piece.
Only finalize a statement if you successfully prepare it.
Be sure to close a database if you open it.
Use modern syntax for accessing values from dictionaries and arrays. It's easier to read and type.
Use modern loops when possible.
Use whitespace. It makes the code easier to read.
Give all of your method parameters a name.
But your SQLite code was actually better than most posts here. You actually use sqlite_bind_xxx to bind values to your query. Too few people do that.
In your code where you're grabbing the values, you are grabbing the row data with:
NSDictionary *rowData=[data objectAtIndex:0];
I suspect you mean:
NSDictionary *rowData=[data objectAtIndex:i];

Taking too much time to retrieve rows from SQLite

I am new to iOS. I am trying to implement a SQLite database.
My problem is that when I go to fetch data from database, it takes a lot of time even though there are only 50 records inside the tables.
I did make a separate method for fetching data because many times I need to retrieve data for different tables.
My method:
-(NSArray*)fetch_data:(NSString *)table_name fields_arr:(NSMutableArray *)fields_arr{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = dirPaths[0];
databasePath = [[NSString alloc] initWithString:[docsDir stringByAppendingPathComponent: #"test.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
sqlite3_stmt *SelectStatement;
NSMutableDictionary *record_dict;
NSString *SelectQry=#"select ";
NSString *lastField = [fields_arr lastObject];
for (int i =0; i<[fields_arr count]; i++) {
if (fields_arr[i] == lastField) {
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"%# ",fields_arr[i]]];
} else {
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"%#,",fields_arr[i]]];
}
}
SelectQry = [SelectQry stringByAppendingString:[NSString stringWithFormat:#"from %#",table_name]];
const char *query_stmt = [SelectQry UTF8String];
NSMutableArray *resultArray = [[NSMutableArray alloc]init];
if (sqlite3_prepare_v2(database,
query_stmt, -1, &SelectStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(SelectStatement) == SQLITE_ROW)
{
record_dict=[[NSMutableDictionary alloc]init];
for (int i = 0; i<[fields_arr count]; i++) {
NSString *field_value = [[NSString alloc]init];
field_value = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(SelectStatement, i)];
[record_dict setValue:field_value forKey:fields_arr[i]];
}
[resultArray addObject:record_dict];
}
return resultArray;
//sqlite3_reset(statement);
sqlite3_finalize(SelectStatement);
}
else{
sqlite3_finalize(SelectStatement);
return nil;
}
}
In your code you are opening database connection for each time method is called for fetching data. This is not appropriate way. You must establish connection only once and use it.
+(sqlite3 *)openDatabase{
if (database == NULL) {
NSString* str_path=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [str_path stringByAppendingPathComponent:strDatabaseName];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
NSLog(#"Database Successfully Opened");
}
else {
NSLog(#"Error in opening database");
database = NULL;
}
}
return database;}
You can call this on app launch from app delegate and define this method in a class where you are defining other DB methods. I hope it will reduce some overhead and will reduce some time too.

Populating array from select query in iOS

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

Resources