How do I get JSON back from a Sqlite text field? - objective-c

I've managed to store some JSON to a field in my sqlite database, the length of the TEXT (in the sqlite docs theres no limit on a text field size) field is 1337.
I've even tried varchar, but again my app crashes with SGABRT. I don't get any other error details.
Looking at the record in a sqlite utility the data is complete and fine and my query shown below works.
I've even substituted why query and the code does work with a record in another table.
I know I should be using Core Data but this is an existing app and I can't convert it at this time.
No sure how to proceed ?
NSString *ret = #"";
const char *sql = "select value from MyTable where item = 'json'";
sqlite3 *database;
int result = sqlite3_open(... db path function ...], &database);
... snip in not db and error code ...
sqlite3_stmt *statementTMP;
sqlite3_prepare_v2(database, sql, -1, &statementTMP, NULL);
if (sqlite3_step(statementTMP) == SQLITE_ROW) {
ret = [[NSString alloc] initWithUTF8String:
(char *)sqlite3_column_text(statementTMP, 1)]; << Fails here
}
sqlite3_finalize(statementTMP);
sqlite3_close(database);
EDIT
Start of my JSON data
{"lowestday":"31","pfAppAdvAcSel":-1,"styleselec
Further EDIT
char *test = (char *)sqlite3_column_text(statementTMP, 1);
NSLog(#"value = %s", test); << (NULL)

The problem is that you are calling sqlite3_column_text() incorrectly. Since you are selecting only a single column (value), there is only one column from which you can extract text. In SQLite3, column numbers begin with 0, rather than 1. Therefore, you should change the second argument of your call to sqlite3_column_text with 0 instead of 1.

Related

SQLite PRAGMA cache_size iOS

I am working on a Keyboard extension on iOS using Objective C where I am using SQLite. I need to understand a few concepts about SQLite which I didn't get by googling. Let me divide the question in parts.
PART: 1
I have come across a PRAGMA in SQLite called PRAGMA cache_size = pages;
The default size here is 2000 pages. Comparing with the default, according to my understanding,
cache_size > 2000 means more memory usage, more speed (than default).
cache_size < 2000 means less memory usage, less speed (than default).
Am I correct here?
PART: 2
I am trying to change the cache_size in the following way,
if (sqlite3_exec(sqlite3Database, [#"PRAGMA CACHE_SIZE=50;" UTF8String], NULL, NULL, NULL) == SQLITE_OK) {
NSLog(#"Successfully changed cache size");
}
else
NSLog(#"Error: failed to set cache size with message %s.", sqlite3_errmsg(sqlite3Database));
I am using this after opening the database. The following code shows it,
-(void)runQuery:(const char *)query isQueryExecutable:(BOOL)queryExecutable{
// Create a sqlite object.
sqlite3 *sqlite3Database;
// Set the database file path.
NSString *databasePath = [self getDbFilePath];
// Initialize the results array.
if (self.arrResults != nil) {
[self.arrResults removeAllObjects];
self.arrResults = nil;
}
self.arrResults = [[NSMutableArray alloc] init];
// Open the database.
BOOL openDatabaseResult = sqlite3_open([databasePath UTF8String], &sqlite3Database);
if(openDatabaseResult == SQLITE_OK) {
if (sqlite3_exec(sqlite3Database, [#"PRAGMA CACHE_SIZE=50;" UTF8String], NULL, NULL, NULL) == SQLITE_OK) {
NSLog(#"Successfully changed cache size");
}
else
NSLog(#"Error: failed to set cache size with message %s.", sqlite3_errmsg(sqlite3Database));
// Declare a sqlite3_stmt object in which will be stored the query after having been compiled into a SQLite statement.
sqlite3_stmt *compiledStatement;
// Load all data from database to memory.
BOOL prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, query, -1, &compiledStatement, NULL);
if(prepareStatementResult == SQLITE_OK) {
// Check if the query is non-executable.
if (!queryExecutable){
// In this case data must be loaded from the database.
// Declare an array to keep the data for each fetched row.
NSMutableArray *arrDataRow;
// Loop through the results and add them to the results array row by row.
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Initialize the mutable array that will contain the data of a fetched row.
arrDataRow = [[NSMutableArray alloc] init];
// Get the total number of columns.
int totalColumns = sqlite3_column_count(compiledStatement);
// Go through all columns and fetch each column data.
for (int i=0; i<totalColumns; i++){
// Convert the column data to text (characters).
char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);
// If there are contents in the currenct column (field) then add them to the current row array.
if (dbDataAsChars != NULL) {
// Convert the characters to string.
[arrDataRow addObject:[NSString stringWithUTF8String:dbDataAsChars]];
}
}
// Store each fetched data row in the results array, but first check if there is actually data.
if (arrDataRow.count > 0) {
[self.arrResults addObject:arrDataRow];
}
}
}
else {
// This is the case of an executable query (insert, update, ...).
// Execute the query.
int executeQueryResults = sqlite3_step(compiledStatement);
if (executeQueryResults == SQLITE_DONE) {
// Keep the affected rows.
self.affectedRows = sqlite3_changes(sqlite3Database);
// Keep the last inserted row ID.
self.lastInsertedRowID = sqlite3_last_insert_rowid(sqlite3Database);
}
else {
// If could not execute the query show the error message on the debugger.
NSLog(#"DB Error: %s", sqlite3_errmsg(sqlite3Database));
}
}
}
else {
// In the database cannot be opened then show the error message on the debugger.
NSLog(#"db error: %s", sqlite3_errmsg(sqlite3Database));
}
// Release the compiled statement from memory.
sqlite3_finalize(compiledStatement);
}
// Close the database.
sqlite3_close(sqlite3Database);
}
But, when I call the method, sqlite3_exec(sqlite3Database, [#"PRAGMA CACHE_SIZE=50;" UTF8String], NULL, NULL, NULL), it always gives SQLITE_OK no matter what I do.
For example, if I do sqlite3_exec(sqlite3Database, [#"abcd bla bla" UTF8String], NULL, NULL, NULL), it returns SQLITE_OK!!
Why is that so?
PART: 3
I want to increase the speed of execution of my queries, but at the same time don't want to use IMDB as the size of the database is huge.
So is PRAGMA page_size = bytes; make any relevance in this case? If yes, then how to do it in Objective C?
Any help is appreciated. Thanks and regards.

Inserting an inverted exclamation mark into a sqlite table

For the life of me I can't figure this out, i've been browsing the web and can't find an answer but also been coding all day so my brain is fried. Currently i'm prepending the inverted exclamation mark to a string like this
NSString *randomString = [NSString stringWithFormat:#"%#random!", #"\u00A1"];
which logs what I want which is
#"¡random!"
So then I insert it into my sqlite database and use the string value given above like so
if(!(self.dbOpen))
{
[self openDatabaseWithSQLName:#"SMCachedDB.db"];
}
sqlInsertStatement = [NSString stringWithFormat:#"INSERT INTO %#(%#) VALUES(%#)", useTable, myColumns, myValues];
char *error;
// Execute statement
if(sqlite3_exec(cachedDatabase, [sqlInsertStatement UTF8String], NULL, NULL, &error) == SQLITE_OK)
{
hasBeenAdded = YES;
NSLog(#"Entry added into '%#' table", useTable);
} else {
NSLog(#"ERROR: %s", sqlite3_errmsg(cachedDatabase));
NSLog(#"ERROR inserting '%#'", myValues);
hasBeenAdded = NO;
}
the string is within the "myValues" string so everything is getting stored correctly but when I select back the values I get the unicode characters before my string like so
"\U00a1random!";
I'm sure I'm missing something dumb. Any help would be greatly appreciated.
I figured it out. It stores the string with the actual unicode characters prepended to it so when I was logging the data stored in the database it returned
"\u00A1random!"
I thought the it would've stored the variable as "¡" and not the actual unicode. So when I selected the value from the database and logged it it returned the value that was necessary which was
"¡random!"
After coming back to it I seemed to figure it out within 5 minutes after spending all that time last night on it, sorry guys.

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.

Retrieving an integer value from a sqlite3 db (problem in obj-c)

In a sqlite3 database, I've a table "data" with two fields: type and path. The field type is defined as INTEGER. In this field I insert a NSUInteger value (which will be for example 0 or 1). The problem is that, when I retrieve it, I obtain a "strange" value. I don't know where I'm wronging.
if (init_statement == nil) {
const char *sql = "SELECT type,path FROM data WHERE id=?";
if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
NSAssert1(0, #"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
}
sqlite3_bind_int(init_statement, 1, primaryKey);
if (sqlite3_step(init_statement) == SQLITE_ROW) {
int type = (int)sqlite3_column_text(init_statement, 0);
char *relPath = (char *)sqlite3_column_text(init_statement, 1);
// other stuff
}
// Reset the statement for future reuse.
sqlite3_reset(init_statement);
SQLite allows only 64 bit signed integers. You are assigning it an unsigned integer. Change it to NSInteger instead.

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?