What is the max size of type "SQLITE3_INTEGER"? - php

As mentioned in https://www.sqlite.org/datatype3.html :
INTEGER: The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8
bytes depending on the magnitude of the value.
The problem is that the statement below gives the desired result as the bound values are comparatively smaller (e.g, $roll_no = 1111111111), however, the execution of statement fetches no result when the bound value is bigger(e.g, $roll_no =3333333333) whereas the SQLite table already holds record with that bigger value.
Is it because the parameter value is truncated or any other reason? What is to be done to get rid of it?
$stmt1 = $db->prepare("select sname,reg_no from student where roll_no=:roll_no");
$stmt1->bindParam(':roll_no', $roll_no, SQLITE3_INTEGER);

See the PDO driver of PHP:
https://github.com/php/php-src/search?q=SQLITE_INTEGER&unscoped_q=SQLITE_INTEGER
#if LONG_MAX <= 2147483647
if (val > ZEND_LONG_MAX || val < ZEND_LONG_MIN) {
ZVAL_STRINGL(data, (char *)sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column));
It supports returning larger integers, but only as strings.
There's no way to have 3333333333 as native integer on PHPs end (32-bit versions). Such it would become a float before it even reaches SQLite.
What you should do is not trying to bind it as integer. Use a string. SQL does type casting of its own. You could likely keep the column as INTEGER on SQLites end even. It's only PHP you have to work around. Or you know, do the overdue upgrade.

Related

Unit testing with Laravel assertDatabaseHas and a float column

Doing some tests on my Laravel app and am running into intermittent failures due to one of my database columns being defined as a FLOAT. When running a test with assertDatabaseHas() it sometimes fails due to floating point uncertainty.
<?php
namespace Test\Unit;
use App\Foo;
class MyModelTest extends TestCase
{
public function testFooCanBar(): void
{
$stringvalue = "baz";
$floatvalue = 234.281;
$data = [
"name" => $stringvalue,
"data" => $floatvalue,
];
Foo::bar($stringvalue, $floatvalue);
$this->assertDatabaseHas("foos", $data);
}
}
Sometimes result:
Failed asserting that a row in the table [foos] matches the attributes {
"name": "baz",
"data": 234.281
}.
Found similar results: [
{
"name": "baz",
"data": 234.280999999999999995
}
]
I can think of a few ways to work around this (change the column to an INT and multiply by 10^x, or just remove the column from the comparison) but I'm wondering if there are any methods I missed to properly check this. When comparing values directly we can use PHPUnit's assertEqualsWithDelta(); is there anything similar for a database check?
In MySQL databases (currently v8), a FLOAT or DOUBLE data type is always stored as an approximate value:
The FLOAT and DOUBLE types represent approximate numeric data values. MySQL uses four bytes for single-precision values and eight bytes for double-precision values.
— from: MySQL FLOAT and DOUBLE Data Type Reference
On the other hand, a DECIMAL data type allows you to store exact decimal values up to 65 digits long.
The DECIMAL and NUMERIC types store exact numeric data values. These types are used when it is important to preserve exact precision, for example with monetary data.
— from: MySQL DECIMAL Data Type Reference
Problems with FLOAT (reference)
MySQL documentation covers the issue with FLOAT and DOUBLE data types not being exact and shows a possible solution to use these types with delta differences at a database levels (full example in the referenced link):
SELECT i, SUM(d1) AS a, SUM(d2) AS b
FROM t1
GROUP BY i
HAVING ABS(a - b) > 0.0001;
Important Note: Keep in mind that FLOAT and DOUBLE are also platform dependant!
A SELECT statement like the one below could either return 0 and -0 or inf and -inf based on the runtime environment:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;

Get values of type number from sqlite - getting text istead

I don't know if it is a PHP issue, or PDO issue, or SQLITE issue, but when I search for values in columns which have NUMBER data type, or simply retrieve a SUM of them, php get them as string istead of number (integer, float...)
<?php
//...
$res = $pdo->query("SELECT SUM(id) as 'foo' FROM anytable");
$bar = $res->fetch(); //this value should be (int)
$bar['test'] = 12.50; //this value is (float)
$bar['test'] = 666; //this value is (int)
echo json_encode($bar);
?>
I get this object:
{ foo: "580", test: 12.50, test2: 666 }
This is a sample code, my real query is really more complex and I don't want to parse every field (roughly 90) as integer or float, since they are mixed data type
EDIT: As noted by VolKerk, the implementation in PDO for sqlite is fairly poor. I tested the feature proposed $pdostmt->getColumnMeta( $intColId ) which give us some hint. Since they don't match syntactically the sqlite driver types, I'll test something more and leave here result by time, if anyone is interested.
$hints = $pdostmt->getColumnMeta( $intColId );
$coltype = $hints['native_type'];
results:
sqlite data type | native_type
---------------------------------
DECIMAL | 'double'
Though there are sqlite functions like sqlite3_column_double() or sqlite3_column_int() which would allow to return a range of "native" types, the function behind PDOStatement::fetch() when using the sqlite driver is currently limited to NULL, sqlite3_column_blob() and as the default for any other type: sqlite3_column_text(). So, regardless of whether the storage type is INTEGER,REAL or TEXT, you will get ...text.
But at least PDOStatement::getColumnMeta can tell you a bit more about the native type. The sqlite driver distinguishes between SQLITE_NULL, SQLITE_FLOAT, SQLITE_BLOB, SQLITE_TEXT and SQLITE_INTEGER.

Compare if value is within range of number mysql

In my database i have 2 columns namely: Min and Max they are varchar and they have number values which i need to compare with. My query is something like this
SELECT * FROM Pricing_tbl WHERE Productid='10'
and i have this to compare the price
while ($selected_row = $stmt - > fetch(PDO::FETCH_ASSOC)) {
if ($marketval > $selected_row['Min'] && $marketval < $selected_row['Max']) {
$price[] = array('price_level' => $selected_row['price_level']);
}
}
This is ok if values of Min and Max are always numbers. But there is an instance where the value of Max is above. How to compare this situation?
Example values will be
Or should i just change the Max value. Any suggestion is appreciated
Assuming a maximum of above means all the way to infinity and you want to keep the data as is 1, you can just change the condition:
$marketval > $selected_row['Min'] && $marketval < $selected_row['Max']
into something like:
$marketval >= $selected_row['Min'] &&
($selected_row['Max'] == 'above' || $marketval < $selected_row['Max'])
In other words a value is considered under the maximum always if the maximum is the word above. Otherwise the actual (original) comparison decides.
PHP short-circuiting on logical operators will ensure that the second half of the or section will never be evaluated if the first half is true.
Note the change I made to the minimum comparison as well, the use of >= rather than >. As it was, a value like 300000.01 would not have been caught.
You may also want to coerce the numerics in the comparisons with $marketValue. If both $marketValue and $selectedRow['whatever'] are strings, I think they'll still use numeric comparison but I usually try to be explicit so I don't have to think about it :-)
1 There are probably better ways to do this, other than storing what's mostly numeric data as strings just because you want to be able to store the value 'above'.
Both methods below involve converting the column type to a numeric one which will allow better comparisons, including having the DBMS itself work it out rather than having to get all data and post-process it with PHP. Data manipulation is what a DBMS does best so it's generally better to leave that manipulation up to the DBMS for efficiency.
The first is to put a ridiculously large number in the maximum for the 'above' row so that your simple 'in between min and max' check will work. Using the DBMS itself to get the pricing level would be a simple:
select pricing_level
from pricing_tbl
where product_id = '10'
and $market_val >= minval
and $market_val < maxval
That will give you a single row containing the correct pricing level.
The second is to store NULL in that column instead of a string. A nullable numeric column will still work with a slight modification:
select pricing_level
from pricing_tbl
where product_id = '10'
and $market_val >= minval
and ($market_val < maxval or maxval is null)
In both those cases, you may want to translate the upper region (large number or null) to and from the word above when presenting or editing the table itself. This will make the process look the same even though the underlying data has changed.
i have 2 columns namely: Min and Max they are varchar
this is wrong. It should be int or at least decimal values
My query is something like this
this is wrong. Your query should include $marketval to do all the calcs on the database side.
here is an instance where the value of Max is above.
this is wrong. No numerical column should ever contain a string. you can store a big number there. or a NULL, but not a string.

How to compare (min & max) Float values with MySQL?

I've a database with lon and lat geo location data.
Both are saved as float / decimal attribute in the mysql table.
Now I want to compare this stuff like:
(u.user_location_lat <= ".$lat_max." AND u.user_location_lat >= ".$lat_min.") AND
(u.user_location_long <= ".$long_max." AND u.user_location_long >=".$long_min.")
But it does not show any result (and it should!) - but also no error.
How to EASILY solve this (I actually don't want to work with spatial indexes - at least I do not understand how to do)?
Thanks.
I recommend you verify what that statement looks like, after you do the variable substitution.
That is, echo or vardump the contents of the variable containing the SQL text, before you prepare/execute the SQL statement.
There doesn't appear to be anything wrong with the form of the predicates. (These could be written using equivalent BETWEEN comparators, but it's not a problem what you have written.)
It's possible you have the min and max values swapped, or have the longitude and latitude swapped. If that's not the issue, then I suspect that the variables being substituted may be represented in scientific notation, rather than as decimal values.
e.g. the SQL text gets generated
... u.lat <= 1.241E+2 ...
rather than
... u.lat <= 124.1 ...
In the former case, MySQL is going to evaluate that literal as decimal value of 1.241.
(There's a corner case issue when the bounding box crosses the +180/-180 boundary, but I don't see that's likely a problem for most of your values, that's going to be an exceptional case, which you would probably need to setup special test case to actually have happen.)
In order to debug this, you need the actual SQL text that's being sent to the database to be prepared/executed.
(There's not enough information in your question to identify the actual problem.)
Q: How do I get the actual SQL text?
A: Construct your SQL statement as a string into a variable; and then var_dump that variable for debugging:
$sqltext = "SELECT ... WHERE u.lat <= ". $lat_max . " AND u.lat ... ";
var_dump($sqltext);
$stmt = mysqli_prepare($db, $sqltext);

BIGINT in MySQL Changes a my number? Why does it do this?

I'll try and keep this simple. I'm using a BIGINT data type on a MySQL database table. I have a function generating a unique random number that may span anywhere from 12 digits to 13 digest long.
When I insert into the database with a digit that is 12 digits in length, it enters it just fine,
but when I use a value that is 13 digits or longer, it seems like it rounds up or something. here is the
php
$defaultText = 'some string'; //just some string
$web_id = 12;
$element_id = 23112182735363; //case 1 (doesn't work)
//$element_id = 2311218333205; //case 2, does work ()
mysql_query("INSERT INTO tableName (web_id, element_id, content)
VALUES ($web_id, $element_id, '".mysql_real_escape_string($defaultText)."')");
results:
in case one, it inserts a slightly different number, usually rounds up for some reason.
in case two, it inserts the number just fine! Maybe someone can help shed some light on this mystery! Thanks again!
the big int datatype:
bigint(200)
Numbers lose their precision from PHP_INT_MAX onwards. See also: http://www.php.net/manual/en/reserved.constants.php#constant.php-int-max
After that they are turned into floats which have limited precision and causes weird rounding issues. The only way to support BIGINT in PHP is by using strings.
I assumed you were talking about a 32-bit server.
But in my server, PHP seemed not lose the precision.
echo(PHP_INT_MAX . "<br/>");
$big_int = -6174803190685607000;
echo($big_int . '<br/>');
output
9223372036854775807<br/>-6174803190685607000<br/>
Sadly I still got the precision losing. I guessed it might because i used 'i' in prepare statement of mysqli, but I could not prove it.

Categories