SQLite query result is missing first row - objective-c

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).

Related

Xcode - NSInvalidArgumentException', reason: '*** +[NSString stringWithUTF8String:]: NULL cString'

I have the below code that should populate the TableViewController with information from my sqlite file, it lets me add fine, and i can view the file and the information is there, but I'm getting the above error message, and failing miserably at fixing it....
-(NSMutableArray *) stockList
{
NSString *filePath = [self getWritableDBPath];
if(sqlite3_open([filePath UTF8String], &db) == SQLITE_OK)
{
const char *sql = "Select Description, Quantity from StockTable";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with query: %s", sqlite3_errmsg(db));
}
else
{
while (sqlite3_step(sqlStatement)==SQLITE_ROW)
{
Stock * stock = [[Stock alloc] init];
stock.desc = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
[thestock addObject:stock];
stock = nil;
}
}
sqlite3_finalize(sqlStatement);
}
sqlite3_close(db);
return thestock;
}
thanks for any help, currently googling it myself..
connection strings as mentioned below: (reason being it causes a LINK error and states that MyDB is a duplicate in both views)
TableView:
NSString * MyDB2=#"StockDatabase.db";
AddingView:
NSString * MyDB=#"StockDatabase.db";
One (or both) of the columns you are fetching from the database is NULL:
stock.desc = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 1)];
stock.qty = [NSString stringWithUTF8String:(char *)sqlite3_column_text(sqlStatement, 2)];
Guard against that with:
const char *desc = sqlite3_column_text(sqlStatement, 1);
if (desc)
stock.desk = #(desc);
const char *qty = sqlite3_column_text(sqlStatement, 2);
if (qty)
stock.qty = #(qty);
I got this error too. How I solved it is by setting the first column text index to "0" instead of "1". And the error went away.
char *charPrice = (char*) sqlite3_column_text(stmt, 0);
NSString *price = [NSString stringWithUTF8String:charPrice];
char *charName = (char*) sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:charName];

Get rows from a SELECT sql statement

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).

adding items to sqlite3 database is not working

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

Objective c : How to fetch data from sqlite table as per primary key?

Say I am having 1 table called ABC with fields ABCid,ABCname,ABCImagePath,ABCSequence. Now this table contains more than 40 rows inside the same.
Now I have an array of numbers say 1,4,5,7,8 and I want to compare these numbers with ABCid and fetch all the data from table ABC as per these IDs.
+ (void) getSelectedData : (int)ABCId
{
NSString *dbPath = [self getDBPath];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK)
{
NSString *query =[NSString stringWithFormat:#"select * from [ABC] where ABCId=%d",ABCId];
const char *sql = [query cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK)
{
[self getInitialDataToDisplay:dbPath];
ABC *DataObj = [[ABC alloc] initWithPrimaryKey:ABCId];
DataObj.ABCName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
ABCImagePath = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
DataObj.ABCSequence = sqlite3_column_int(selectstmt,8);
[appDelegate.arrGetData addObject:DataObj]; }
else
{
NSAssert1(0,#"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
}
NSLog(#"%d",[appDelegate.DataArrayTrans count]);
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
But app gets crashed in the if condition. Also note that I use below lines from the pageController to call the above function from ABC Object file.
for(int i=0;i<[arrRId count];i++)
{
[ABC getSelectedData:[[arrRId objectAtIndex:i]integerValue]];
[arr576576 addObject:[appDelegate.arrGetData valueForKey:#"ABCId"]];
}
Please guide me where I am making mistake. Thank you.
your query should be like this select * from ABC where ABCId IN (1,4,5,7,8)
that's it.

Retrieving records based on latest entry from sqlite DB and displaying in UITableView

I am working with Sqlite DB for storing and retrieving the data and displaying it in the tableview. My question is how to display the data based on latest record entered in the DB.
Let me explain what I am doing.
Users will enter there data (fields like date,gender,country) and once clicked on save button the data gets stored in sqlite DB.
After saving immediately I will show only the dates in the tableview(next page) that I will be pulling it from the sqlite DB. But when I am displaying the dates in tableview the dates are displayed in ascending order (based on numbers).
But I want to display as per latest record entered by the user. below is the what I am doing in coding.
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
NSString *selectSQL;
if(sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
if ([set_user isEqualToString:#"Anonymous"] && [set_pass isEqualToString:#"Anonymous"])
{
selectSQL = [NSString stringWithFormat: #"SELECT * from Guest where username = '%#' and password = '%#'", set_user,set_pass];
}
else
{
selectSQL = [NSString stringWithFormat: #"SELECT * from Vitals where username = '%#' and password = '%#'", set_user,set_pass ];
}
const char *select_stmt = [selectSQL UTF8String];
if(sqlite3_prepare_v2(contactDB, select_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
sq_date = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 1)];
sq_gender = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 2)];
sq_ethnicity = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 12)];
[date_array addObject:sq_date];
[gender_array addObject:sq_gender];
[ethnicity addObject:sq_ethnicity];
/*if (![date_array containsObject:sq_date])
{
[date_array addObject:sq_date];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"" ascending:NO];
[date_array sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
}*/
}
sqlite3_finalize(statement);
}
}
What you can do is when inserting the data (entered by the user) into sqlite DB, have another column called timestamp & insert the current timestamp (maybe epoch time). This could be done for each row.
When retrieving you can order by timestamp rather than the ids which is what you want.