Retrieving an integer value from a sqlite3 db (problem in obj-c) - objective-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.

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.

sqlite fetch statement

Good day,
I need to fetch rows from my sqlite table, but I need to pass multiple parameters. This is my statement that does not work.
SELECT * FROM messages WHERE currentuser=\"%#\" AND (belongstouser=\"%#\" OR mymsgforuser=\"%#\") ORDER BY ID ASC
I need it to first check for the currentuser match, then out of those matches to check for either the belongstouser or mymsgforuser matches. Is it possible to nest a sqlite statement in this fashion? I tried removing the parenthesis and that didn't work either. I also searched the sqlite documentation and could not find a solution.
I can see wrong SQL syntax. string constants must be quoted with single quotes (') instead of (")
And as rmaddy said, you'd better avoid stringWithFormat. Use prepare statement technique.
- (BOOL)_prepareStatement:(sqlite3_stmt **)statement withSQL:(const char *)sql {
sqlite3_stmt *s = *statement;
//caDatabase is declared as sqlite3 *caDatabase object
if (nil == s && sqlite3_prepare_v2(caDatabase, sql, -1, &s, NULL)!= SQLITE_OK)
{
[self _showError];
*statement = nil;
return NO;
}
*statement = s;
return YES;
}
- (caObjectId)existObject:(caObjectId)objId withType:(caCacheObjectType)objType libraryID:(int)aLibraryID
{
#synchronized (self)
{
const char *caSQLexistObj = "SELECT id FROM objects WHERE objId = ? AND objType = ? AND libraryID = ?";
if(![self _prepareStatement:&ca_existObjectStatement withSQL:caSQLexistObj]) {
//produce some error message
return;
}
sqlite3_bind_int(ca_existObjectStatement, 1, objId);
sqlite3_bind_int(ca_existObjectStatement, 2, objType);
sqlite3_bind_int(ca_existObjectStatement, 3, aLibraryID);
NSInteger result = sqlite3_step(ca_existObjectStatement);
if (result != SQLITE_ROW)
{
sqlite3_reset(ca_existObjectStatement);
return caObjectIdNone;
}
caObjectId cacheId = sqlite3_column_int(ca_existObjectStatement, 0);
sqlite3_reset(ca_existObjectStatement);
return cacheId;
}
}

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

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.

sqlite3 COUNT in iOS (specifically ON the iPhone/iPad)

I know that the simulator and the actual iOS hardware are not EXACTLY the same, but I'm starting to pull my hair out over this one. I have this code:
sqlite3 *database;
sqlite3_stmt *statement;
int themeCount;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSString *updateSQL = [NSString stringWithFormat: #"SELECT COUNT(*) FROM Theme"];
const char *update_stmt = [updateSQL UTF8String];
if(sqlite3_prepare_v2(database, update_stmt, -1, &statement, NULL) == SQLITE_OK){
if(sqlite3_step(statement)==SQLITE_ROW)
{
themeCount = sqlite3_column_int(statement, 0);
}
}
sqlite3_finalize(statement);
sqlite3_close(database);
}
With the simulator, it works perfectly fine. Once I push it to my devices, it fails. I've broken it down and came up with the return code where it fails:
if(sqlite3_prepare_v2(database, update_stmt, -1, &statement, NULL) == SQLITE_OK)
If I change that line to capture the code (ie. int x = sqlite3_prepare_v2(...)) it returns 0 with the simulator, 1 with the device. What am I doing wrong here?!?!
Also, for the record, the CREATE statement for the Theme table is:
#"CREATE TABLE Theme (ThemeId INTEGER PRIMARY KEY, ThemeName TEXT, Available BIT);"
(My first thought is that it was case sensitive)
You are not opening the database you think you are opening. The sqlite3_prepare_v2 is the first statement that needs the schema to be present. I suspect your databasePath is incorrect.
You can be more specific with sqlite3_open_v2 by omitting the SQLITE_OPEN_CREATE flag which is the default with sqlite3_open so you don't notice that a new database is being created by the open call. See SQLite3 docs. With the result of
sqlite3_open_v2([databasePath UTF8String], &database, SQLITE_OPEN_READWRITE, NULL)
you will see that the database does not exist.

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)