sqlite3 datetime not functioning properly on iPhone - sql

I am having trouble getting sqlite3 to do comparisons between datetimes. On my mac, when I run the sql queries, it works. However, on the iPhone, the exact same query fails.
I have a sqlite3 table setup as follows:
CREATE TABLE IF NOT EXISTS `Artists` (
`id` int(11) NOT NULL PRIMARY KEY,
`name` varchar(256) NOT NULL,
`lastUpdate` date NOT NULL default CURRENT_TIMESTAMP ,
...
...
);
I can insert an artist with:
INSERT OR IGNORE INTO `Artists` (`id`,`name`) VALUES ('1','Justin');
I am trying to find the number of artists that have not been updated for the last 2 seconds (to keep it short):
SELECT COUNT(*) FROM `Artists` WHERE `id` = ? AND `lastUpdate` < datetime('now','-2 seconds');
On the iPhone, this returns 0. On my pc, it returns 1. I am expecting the value to be 1. What is the difference? I am retrieving the value by:
-(BOOL) artistNeedsUpdating:(int)artistId
{
[dbLock lock];
BOOL result = NO;
sqlite3_stmt *statement;
const char* query = "SELECT COUNT(*) FROM `Artists` WHERE `id` = ? AND `lastUpdate` < datetime('now','-2 seconds');";
if(sqlite3_prepare_v2(database, query, -1, &statement, NULL) == SQLITE_OK){
sqlite3_bind_int(statement, 1, artistId);
if(sqlite3_step(statement) == SQLITE_ROW){
if(sqlite3_column_int(statement, 0) > 0) // If 0, then we don't need to update
result = YES;
}
sqlite3_finalize(statement);
}
[dbLock unlock];
return result;
}
I am confused as to why it works on one platform but not the other, does anybody have any ideas?
Thank you,
Justin

Nevermind, it was my mistake.
I was updating the row after it got done with
UPDATE `Artists` SET `lastUpdate`='CURRENT_TIMESTAMP';
When it should have been:
UPDATE `Artists` SET `lastUpdate`=CURRENT_TIMESTAMP;

Related

Equivalent of sqlite3_column_int to get possible NULL values

in my app, I am using a SQLite database to store some data. One of the integer fields is optional, so it is defined like so:
I have the following CREATE statement:
CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp FLOAT NOT NULL, testProperty INT);
I can get the float property using sqlite3_column_double. For the int column I could use 'sqlite3_column_int' but this always returns a value (0) even if the row does not contain a value.
How can I check if the row actually has a value for this property?
I have the following code to get all rows:
var statement: OpaquePointer? = nil
let sql = "SELECT * FROM Test;"
sqlite3_prepare_v2(self.connection, sql, -1, &statement, nil)
while sqlite3_step(statement) == SQLITE_ROW
{
let testProperty = sqlite3_column_int(statement, 2) // always returns 0
}
sqlite3_finalize(statement)
You can use sqlite3_column_type() to check if it's SQLITE_INTEGER or SQLITE_NULL.

Modify records while iterating over them in SQLite

I'm trying to update all the columns of an SQLite database if it meets certain conditions.
I can't just use an UPDATE statement, the actual production database has diverse values, think of it as a game with many "bases" and I need to simulate them producing and consuming resources, and I'm not sure how to do that in SQL.
Here's a Python script to create the sample database where I'm testing this.
Now, the callback's code to modify the table column returns code 5, and the values are not being updated. I'm not sure how to fix this.
import sqlite3
import uuid
db = sqlite3.connect("main.db")
cur = db.cursor()
cur.execute("DROP TABLE IF EXISTS things")
cur.execute("CREATE TABLE IF NOT EXISTS things (numid INTEGER PRIMARY KEY, uuid TEXT, value INTEGER)")
for i in range(1, 2000):
cur.execute(
"INSERT INTO things (uuid, value) VALUES (?, ?)", (str(uuid.uuid4()), 1)
)
db.commit()
And this is the C code that I'm using to go through the entire database. Make sure to link with the SQLite library.
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct things_t
{
int numid;
int value;
} things_t;
static int callback(void *voidthing, int argc, char **argv, char **azColName)
{
int i;
things_t *thing = (things_t *)voidthing;
for (i = 0; i < argc; i++)
{
if (strcmp(azColName[i], "value") == 0)
{
thing->value = atoi(argv[i]);
thing->value++;
}
if (strcmp(azColName[i], "numid") == 0)
{
thing->numid = atoi(argv[i]);
}
}
sqlite3 *db;
int rc = sqlite3_open("main.db", &db);
printf("open file rc: %d (thing numid: %d, thing value: %d)\n", rc,
thing->numid, thing->value);
sqlite3_stmt *stmt;
sqlite3_prepare(db, "UPDATE things SET value = ? WHERE numid = ?", -1,
&stmt, NULL);
sqlite3_bind_int(stmt, 1, thing->value);
sqlite3_bind_int(stmt, 2, thing->numid);
rc = sqlite3_step(stmt);
printf("step rc: %d (thing numid: %d, thing value: %d)\n", rc,
thing->numid, thing->value);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
int main()
{
char *err = "";
sqlite3 *db;
sqlite3_open("main.db", &db);
things_t thing;
sqlite3_exec(db, "SELECT * FROM things LIMIT 200", callback, &thing,
&err);
sqlite3_close(db);
}
I've decided to just use SQL subqueries to do this.
For example, this query would show how much resources each faction would lose or gain per turn (once again, continuing the game example).
SELECT (SELECT faction_name FROM factions WHERE owner = numid) as owner, SUM(production - consumption) as resdiff FROM buildings GROUP BY owner;
Here's a dump of the sample database where these queries (including the one above) are being tested:
BEGIN TRANSACTION;
CREATE TABLE factions (numid INTEGER PRIMARY KEY, faction_name TEXT, resources INTEGER);
INSERT INTO factions VALUES(1,'Arch users',900);
INSERT INTO factions VALUES(2,'Debian users',912);
INSERT INTO factions VALUES(3,'Gentoo users',100);
CREATE TABLE buildings (numid INTEGER PRIMARY KEY, owner INTEGER, production INTEGER, consumption INTEGER, FOREIGN KEY(owner) REFERENCES factions(numid));
INSERT INTO buildings VALUES(1,1,339,291);
INSERT INTO buildings VALUES(2,1,211,440);
INSERT INTO buildings VALUES(3,1,773,445);
INSERT INTO buildings VALUES(4,2,143,498);
INSERT INTO buildings VALUES(5,2,339,113);
INSERT INTO buildings VALUES(6,3,10,9);
INSERT INTO buildings VALUES(7,2,144,14);
COMMIT;
I'll just update the data with SQL, then extract the results with other queries (and as part of the game example, presumably show them to the player).

How to bind a LIKE wildcard in SQLite3

I'd like to run the SQL query: SELECT COUNT(id) AS nbr FROM user WHERE name LIKE '%John%', and get the number of records matched by the keyword John.
So I've written the following code :
unsigned int nbr;
char *sql, *like;
sqlite3 *db;
sqlite3_stmt *stmt;
/* Database connection etc. */
like = _make_string("%%%s%%", keyword); /* keyword: John */
printf("Like clause: %s\n", like);
sql = _make_string("SELECT COUNT(id) AS nbr FROM user WHERE name LIKE ?");
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, like, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) {
/* error, SQL execution failed */
}
nbr = sqlite3_column_int64(stmt, 1);
printf("Number of record: %u\n", nbr);
/* Free strings, finalize statement, disconnect db etc. */
For reason of simplicity, some return value tests are ignored in the snippet. _make_string(const char *fmt, ...) is a printf-like function which can form strings, and it did return %John% to me.
However, my code always returns 0 record. I have tried the SQL in sqlite3 command line shell, but it has found 2 records.
Any idea please ?
I've hard coded the LIKE clause and removed the sqlite3_bind_text function, and it doesn't change the result.
sql = _make_string("SELECT COUNT(id) AS nbr FROM client WHERE name LIKE '%John%'");
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (sqlite3_step(stmt) != SQLITE_ROW) {
/* error */
}
nbr = sqlite3_column_int64(stmt, 1);
printf("Number of record: %u\n", nbr);
The program always returns 0 records.
Well, I've found the answer in the mailing list archive of SQLite Why bind indexes start from 1 and column indexes start from 0?
In fact, the bind index starts from 1, but the column index starts from 0. So the code should be modified as the following
nbr = sqlite3_column_int64(stmt, 0);
printf("Number of record: %u.\n", nbr);
Now the program returns 2 (records) !

iOS - SQLite not updating but prepare and step are successfully executing

The title is pretty much all the information I have because I cannot find an issue with the code. The same instance of database that is being used in the code below is able to successfully insert, delete and select, but this function does not work. No error code is retured(Always 0), and the program continues to run nominally after executing this function.
Here is the table information:
reminder (uniqueID integer primary key autoincrement, title text not null, details text, time integer)
Code in question:
- (void) updateEntryData:(ReminderData *)data
{
sqlite3_stmt *statement;
char *updateCommand = "UPDATE reminder SET title = '?', details = '?', time = ? WHERE uniqueID = ?";
int e = sqlite3_prepare_v2(database, updateCommand, -1, &statement, nil);
if(e != SQLITE_OK) {
NSLog(#"Problem with updateEntryWithUniqueID");
NSLog(#"Error Code: %d, message '%s'", e, sqlite3_errmsg(database));
return;
}
sqlite3_bind_text(statement, 1, [data.title UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(statement, 2, [data.details UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(statement, 3, [data.time timeIntervalSince1970]);
sqlite3_bind_int(statement, 4, data.uniqueID);
NSLog(#"\nID: %d\nTitle: %#\nDetails: %#\nTime: %#", data.uniqueID, data.title, data.details, data.time);
if(sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"Problems updating entry in reminder");
}
/* Finished */
sqlite3_finalize(statement);
}
Any help would be greatly appreciated; I am stumped.
Edit: Forgot to mention that the sql command used works when entered into the sqlite console.
Just remove the single quotes around the parameters. Wrapping the placeholders will make it a value and not a parameter anymore.
char *updateCommand = "UPDATE reminder SET title = ?, details = ? .....";

Delete column from Sqlite database

Basically I have to enter all textfield values in database then have to use for sending to webservice. So when the one column details send to service then have to delete that column.
I have done this:
-(void)deleteTableDataFromSavedDataTable:(NSString *)lastID {
NSString *sql_str=[NSString stringWithFormat:#"DELETE FROM FormInfoValues where Phone = %#",lastID];
const char *sql = [sql_str UTF8String];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
NSLog(#"sql delete statement is ");
sqlite3_stmt *deleteStmt;
if(sqlite3_prepare_v2(database, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
NSLog(#"sql delete statement is %#", deleteStmt);
if(sqlite3_step(deleteStmt) != SQLITE_DONE )
{
NSLog( #"Error: %s", sqlite3_errmsg(database) );
}
else
{
NSLog( #"row id = %lld", (sqlite3_last_insert_rowid(database)+1));
NSLog(#"No Error");
}
}
sqlite3_finalize(deleteStmt);
}
sqlite3_close(database);
}
but its not deleting after sending. Why its not calling?
give me ideas..
It's quite possible I'm not understanding your issue properly -- so I apologize in advance if so.
Regarding: "...then have to delete that column."
-- If you want to clear the data in a column (by setting it to zero, or empty string, or NULL) then you'll want to use the UPDATE command in SQL. The DELETE command always deletes ROWS.
-- If you really must remove a column from a table's schema, you'll have to create a new table in sqlite. (Sqlite allows you to ADD a column via ALTER TABLE command, and some other databases DO allow you to drop a column using ALTER TABLE as well.) You can quickly copy a table (without it's constraints, etc) via e.g.:
CREATE TABLE MyOutput as SELECT a,b,d,f,h,z from MyOriginal;
-- If your output is created by a SELECT statement, just avoid using "*" and specify just the columns you want included.