Strange result from SQLite query using Swift - ios

I have a table in my DB. In that table, I have a value stored (it's a date, in milliseconds): "1424386800000", in a column named "fechainicio".
I have downloaded SQLite browser in my Mac to actually check that the value is stored, and its correct, and it is.
Later, in my code, I retrieve that value using this:
var fechaInicio:Int64=Int64(results!.intForColumn("fechainicio"))
When I do this, I am receiving the value "-1542342272".
Why is the value retrieved incorrectly?
I have tried also
var fechaInicio:Int64=results!.intForColumn("fechainicio").toIntMax()
and
var fechaInicio=results!.intForColumn("fechainicio")
Isn't Int64 the correct type to retrieve a long value from a database?

The int type from (Objective-)C is a 32-bit integer type (and mapped to Int32 in Swift). This means that the
- (int)intForColumn:(NSString*)columnName;
method from FMDB can return only 32-but values. This method ultimately calls the SQLite function
int sqlite3_column_int(sqlite3_stmt*, int iCol);
which returns the lower 32-bit of the value, without any overflow check.
The proper solution is to use
- (long)longForColumn:(NSString*)columnName;
instead which returns Int64 in Swift.

Related

TFDQuery and SQLite: Type mismatch for field, expecting: LargeInt actual: WideString

Using Delphi 10.2, SQLite and Teecharts. My SQLite database has two fields, created with:
CREATE TABLE HistoryRuntime ('DayTime' DateTime, Device1 INTEGER DEFAULT (0));
I access the table using a TFDQuery called qryGrpahRuntime with the following SQL:
SELECT DayTime AS TheDate, Sum(Device1) As DeviceTotal
FROM HistoryRuntime
WHERE (DayTime >= "2017-06-01") and (DayTime <= "2017-06-26")
Group by Date(DayTime)
Using the Field Editor in the Delphi IDE, I can add two persistent fields, getting TheDate as a TDateTimeField and DeviceTotal as a TLargeIntField.
I run this query in a program to create a TeeChart, which I created at design time. As long as the query returns some records, all this works. However, if there are no records for the requested dates, I get an EDatabaseError exception with the message:
qryGrpahRuntime: Type mismatch for field 'DeviceTotal', expecting: LargeInt actual: Widestring
I have done plenty of searching for solutions on the web on how to prevent this error on an empty query, but have had not luck with anything I found. From what I can tell, SQLite defaults to the wide string field when no data is returned. I have tried using CAST in the query and it did not seem to make any difference.
If I remove the persistent fields, the query will open without problems on an empty return set. However, in order to use the TeeChart editor in the IDE, it appears I need persistent fields.
Is there a way I can make this work with persistent fields, or am I going to have to throw out the persistent fields and then add the TeeChart Series at runtime?
This behavior is described in Adjusting FireDAC Mapping chapter of the FireDAC's SQLite manual:
For an expression in a SELECT list, SQLite avoids type name
information. When the result set is not empty, FireDAC uses the value
data types from the first record. When empty, FireDAC describes those
columns as dtWideString. To explicitly specify the column data type,
append ::<type name> to the column alias:
SELECT count(*) as "cnt::INT" FROM mytab
So modify your command e.g. this way (I used BIGINT, but you can use any pseudo data type that maps to a 64-bit signed integer data type and is not auto incrementing, which corresponds to your persistent TLargeIntField field):
SELECT
DayTime AS "TheDate",
Sum(Device1) AS "DeviceTotal::BIGINT"
FROM
HistoryRuntime
WHERE
DayTime BETWEEN {d 2017-06-01} AND {d 2017-06-26}
GROUP BY
Date(DayTime)
P.S. I did a small optimization by using BETWEEN operator (which evaluates the column value only once), and used an escape sequence for date constants (which, in real you replace by parameter, I guess; so just for curiosity).
This data type hinting is parsed by the FDSQLiteTypeName2ADDataType procedure that takes and parses column name in format <column name>::<type name> in its AColName parameter.

max value of string in realm

I intended to get the max value of string type in Realm by maxOfProperty method as follows:
[[MyRealmObject allObjects] maxOfProperty:propertyName]
However, I encountered the following error:
maxProperty is not supported for string property
Considering I can't change the type of property, what should I do now?
maxOfProperty is only supported for int, float, double, and NSDate types, see more in docs.
There is no any builtin solution for your case, I'd suggest you to store the maximum value as a separate object and update it everytime you modify the database or just query all objects and find the maximum value by comparing it manually.
We can use this solution
[[[[MyRealmObject allObjects] sortedResultsUsingKeyPath:propertyName ascending:NO] firstObject] objectForKeyedSubscript:propertyName];

Integer store value is negative

I have a Core Data model that contains an id field which is a Integer 16.
I noticed that the app was saving some object with a negative value in that field
For instance (from sqlite3 command line):
2|1|1|-32223||9|424968994|424968994|step`
I thought I'd switch to Integer 32 in order to fix it but the problem still remains. Object are still saved with a negative value in that field. I'm not quite sure why.
Was that right to switch to Integer 32? Should have this fixed the problem?
Edit: Example of value I want to save
For example the value I am storing is 33239 but it's saved as -32297
Being unsigned long compatible with Integer32 and Integer64, you should pass this type of var (unsigned long) to save in CoreData.
I have a similar issue.I am storing the 33675 integer value in CoreData but when i retrieve i got value like -26532.
In My case i defined my integer value as type int16 so range of int16 is -32,768 to 32,767 when i change my variable from int16 to int64 its resolved my issue.
for reference :Swift - Data Types

What is the difference between AsInteger and Value in Delphi?

I want to know the difference between following two statements related to datasets in delphi.
dsMyDataSet.ParamByName('ID').AsInteger := 1122; //If ID is integer
dsMyDataSet.ParamByName('ID').AsString := '1122'; //If ID is string
and
dsMyDataSet.ParamByName('ID').Value := 1122; //ID is string or integer
Do these statements carry same meaning? Does "value" implicitly converts integer into string?
The TParam.AsInteger property, for instance, set the value and the data type of the parameter. TParam.Value does the same, but TParam will decide which type will be mapped to the value inside the Variant and not always it´s the data type you would like.
I advise you to set values by using the AsXXX properties only, since you will be in control of the parameter's data type, what can save you from having parameter binding errors.
So, answering your final question: no, the values won´t be converted to the right data type, you have to set the data type by selecting the right property to assign the value.

Sqlite WHERE clause and Delphi XE longword values

In Delphi XE, I am storing crc32 hash of a string in an SQlite database, in a column declared as INTEGER. My understanding is that SQlite does not distinguish among integer types: int, int64, signed and unsigned, they're all the same as far as the database is concerned. However, when I store a value declared as longword in Delphi, a WHERE clause fails to match on that value later.
My insert statement (trimmed down here) is:
INSERT INTO main VALUES (id, crc) (?, ?);
The longword value gets bound to the second parameter and all goes well. But when I do
SELECT id FROM main WHERE crc = ?;
the query returns no results.
Viewing the database in SQLiteSpy, the longword values are displayed as negative integers. If I execute the above SELECT with the negative value copy-and-pasted from that display, the query returns the expected record.
It would seem that when I bind a longword value to the INSERT statement, SQLIte does something else then when I bind the same longword value to the SELECT statement. Casting the value to integer (in Delphi code, not in SQL) fixes the problem, but it should not be necessary, and it'll be easy to forget to cast in other places. Is there a better solution? Is SQLite behaving correctly?
The problem is not in SQLite, but in the way you're binding your parameters to the SQLite engine.
Depending on the SQlite framework you're using, you must explicitely bound an Int64 to your statement:
INSERT INTO main VALUES (id, crc) (?, ?);
For example, using our framework, with a CRC declared as a Cardinal, you must use this code (and NOT any integer(CRC)):
sqlite3_check(RequestDB,sqlite3_bind_Int64(Request,2,Int64(CRC)));
If the above code doesn't work, this will always work:
var CRC64: Int64;
CRC64Rec: TInt64Rec absolute CRC64;
begin
CRC64Rec.Lo := CRC;
CRC64Rec.Hi := 0;
sqlite3_check(RequestDB,sqlite3_bind_Int64(Request,2,CRC64));
In all cases, without showing your own code about binding parameters, it's difficult to know what's wrong in your code.

Resources