Skip to content

Commit 6ea29ef

Browse files
committed
sqlite can return int for decimal type
1 parent 29dee57 commit 6ea29ef

File tree

4 files changed

+310
-123
lines changed

4 files changed

+310
-123
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ This is due to the design of the `Query` class preventing from determining the h
154154

155155
### Expression types inferring
156156

157-
Whether `MAX(e.id)` is fetched as `string` or `int` highly [depends on drivers, their setup and PHP version](https://github.com/janedbal/php-database-drivers-fetch-test).
158-
This extension copies the logic from linked analysis, autodetects your setup and provides accurate results for `pdo_mysql`, `mysqli`, `pdo_sqlite`, `sqlite3`, `pdo_pgsql` and `pgsql`.
159-
Any other driver will result in union with stringified version, e.g. `numeric-string|int`.
157+
Whether `MAX(e.id)` is fetched as `numeric-string` or `int` highly [depends on drivers, their setup and PHP version](https://github.com/janedbal/php-database-drivers-fetch-test).
158+
This extension autodetects your setup and provides accurate results for `pdo_mysql`, `mysqli`, `pdo_sqlite`, `sqlite3`, `pdo_pgsql` and `pgsql`.
159+
Any other driver will result in mixed.
160160

161161
### Problematic approaches
162162

src/Type/Doctrine/Descriptors/DecimalType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public function getDatabaseInternalType(): Type
3434
{
3535
return TypeCombinator::union(
3636
new FloatType(),
37+
new IntegerType(),
3738
new IntersectionType([
3839
new StringType(),
3940
new AccessoryNumericStringType(),
@@ -46,7 +47,7 @@ public function getDatabaseInternalTypeForDriver(Connection $connection): Type
4647
$driverType = DriverType::detect($connection);
4748

4849
if ($driverType === DriverType::SQLITE3 || $driverType === DriverType::PDO_SQLITE) {
49-
return new FloatType();
50+
return TypeCombinator::union(new FloatType(), new IntegerType());
5051
}
5152

5253
if (

src/Type/Doctrine/Query/QueryResultTypeWalker.php

Lines changed: 93 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,17 @@ public function walkFunction($function): string
414414
return $this->marshalType(IntegerRangeType::fromInterval(0, null));
415415

416416
case $function instanceof AST\Functions\AbsFunction:
417-
// mysql sqlite pdo_pgsql pgsql
418-
// col_float => float float string float
419-
// col_decimal => string float string string
420-
// col_int => int int int int
421-
// col_bigint => int int int int
417+
// mysql sqlite pdo_pgsql pgsql
418+
// col_float => float float string float
419+
// col_decimal => string float|int string string
420+
// col_int => int int int int
421+
// col_bigint => int int int int
422422
//
423-
// ABS(col_float) => float float string float
424-
// ABS(col_decimal) => string float string string
425-
// ABS(col_int) => int int int int
426-
// ABS(col_bigint) => int int int int
427-
// ABS(col_string) => float float x x
423+
// ABS(col_float) => float float string float
424+
// ABS(col_decimal) => string float|int string string
425+
// ABS(col_int) => int int int int
426+
// ABS(col_bigint) => int int int int
427+
// ABS(col_string) => float float x x
428428

429429
$exprType = $this->unmarshalType($this->walkSimpleArithmeticExpression($function->simpleArithmeticExpression));
430430
$exprType = $this->castStringLiteralForFloatExpression($exprType);
@@ -434,20 +434,14 @@ public function walkFunction($function): string
434434
$nullable = $this->canBeNull($exprType);
435435

436436
if ($exprTypeNoNull->isInteger()->yes()) {
437-
$positiveInt = $this->canBeNull($exprType)
438-
? TypeCombinator::addNull(IntegerRangeType::fromInterval(0, null))
439-
: IntegerRangeType::fromInterval(0, null);
440-
return $this->marshalType($positiveInt);
437+
$nonNegativeInt = $this->createNonNegativeInteger($nullable);
438+
return $this->marshalType($nonNegativeInt);
441439
}
442440

443-
if ($exprTypeNoNull->isFloat()->yes() || $exprTypeNoNull->isNumericString()->yes()) {
441+
if ($this->containsOnlyTypes($exprTypeNoNull, [new IntegerType(), new FloatType(), $this->createNumericString(false)])) {
444442
return $this->marshalType($exprType); // retains underlying type
445443
}
446444

447-
if ($exprTypeNoNull->isString()->yes()) {
448-
return $this->marshalType($this->createFloat($nullable));
449-
}
450-
451445
return $this->marshalType(new MixedType());
452446

453447
case $function instanceof AST\Functions\BitAndFunction:
@@ -598,7 +592,7 @@ public function walkFunction($function): string
598592
$exprType = $this->unmarshalType($this->walkSimpleArithmeticExpression($function->simpleArithmeticExpression));
599593
$exprTypeNoNull = TypeCombinator::removeNull($exprType);
600594

601-
if (!$exprTypeNoNull->isFloat()->yes() && !$exprTypeNoNull->isNumericString()->yes() && !$exprTypeNoNull->isInteger()->yes()) {
595+
if (!$this->containsOnlyNumericTypes($exprTypeNoNull)) {
602596
return $this->marshalType(new MixedType()); // dont try to deal with non-numeric args
603597
}
604598

@@ -824,12 +818,27 @@ private function createFloat(bool $nullable): Type
824818
return $nullable ? TypeCombinator::addNull($float) : $float;
825819
}
826820

821+
private function createFloatOrInt(bool $nullable): Type
822+
{
823+
$union = TypeCombinator::union(
824+
new FloatType(),
825+
new IntegerType()
826+
);
827+
return $nullable ? TypeCombinator::addNull($union) : $union;
828+
}
829+
827830
private function createInteger(bool $nullable): Type
828831
{
829832
$integer = new IntegerType();
830833
return $nullable ? TypeCombinator::addNull($integer) : $integer;
831834
}
832835

836+
private function createNonNegativeInteger(bool $nullable): Type
837+
{
838+
$integer = IntegerRangeType::fromInterval(0, null);
839+
return $nullable ? TypeCombinator::addNull($integer) : $integer;
840+
}
841+
833842
private function createNumericString(bool $nullable): Type
834843
{
835844
$numericString = TypeCombinator::intersect(
@@ -840,6 +849,18 @@ private function createNumericString(bool $nullable): Type
840849
return $nullable ? TypeCombinator::addNull($numericString) : $numericString;
841850
}
842851

852+
private function containsOnlyNumericTypes(
853+
Type ...$checkedTypes
854+
): bool
855+
{
856+
foreach ($checkedTypes as $checkedType) {
857+
if (!$this->containsOnlyTypes($checkedType, [new IntegerType(), new FloatType(), $this->createNumericString(false)])) {
858+
return false;
859+
}
860+
}
861+
return true;
862+
}
863+
843864
/**
844865
* @param list<Type> $allowedTypes
845866
*/
@@ -1636,33 +1657,36 @@ public function walkArithmeticTerm($term): string
16361657
*/
16371658
private function inferPlusMinusTimesType(array $termTypes): Type
16381659
{
1639-
// mysql sqlite pdo_pgsql pgsql
1640-
// col_float float float string float
1641-
// col_decimal string float string string
1642-
// col_int int int int int
1643-
// col_bigint int int int int
1644-
// col_bool int int bool bool
1660+
// mysql sqlite pdo_pgsql pgsql
1661+
// col_float float float string float
1662+
// col_decimal string float|int string string
1663+
// col_int int int int int
1664+
// col_bigint int int int int
1665+
// col_bool int int bool bool
16451666
//
1646-
// col_int + col_int int int int int
1647-
// col_int + col_float float float string float
1648-
// col_float + col_float float float string float
1649-
// col_float + col_decimal float float string float
1650-
// col_int + col_decimal string float string string
1651-
// col_decimal + col_decimal string float string string
1652-
// col_string + col_string float int x x
1653-
// col_int + col_string float int x x
1654-
// col_bool + col_bool int int x x
1655-
// col_int + col_bool int int x x
1656-
// col_float + col_string float float x x
1657-
// col_decimal + col_string float float x x
1658-
// col_float + col_bool float float x x
1659-
// col_decimal + col_bool string float x x
1667+
// col_int + col_int int int int int
1668+
// col_int + col_float float float string float
1669+
// col_float + col_float float float string float
1670+
// col_float + col_decimal float float string float
1671+
// col_int + col_decimal string float|int string string
1672+
// col_decimal + col_decimal string float|int string string
1673+
// col_string + col_string float int x x
1674+
// col_int + col_string float int x x
1675+
// col_bool + col_bool int int x x
1676+
// col_int + col_bool int int x x
1677+
// col_float + col_string float float x x
1678+
// col_decimal + col_string float float|int x x
1679+
// col_float + col_bool float float x x
1680+
// col_decimal + col_bool string float|int x x
16601681

16611682
$driverType = DriverType::detect($this->em->getConnection());
16621683
$types = [];
1684+
$typesNoNull = [];
16631685

16641686
foreach ($termTypes as $termType) {
1665-
$types[] = $this->generalizeLiteralType($termType, false);
1687+
$generalizedType = $this->generalizeLiteralType($termType, false);
1688+
$types[] = $generalizedType;
1689+
$typesNoNull[] = TypeCombinator::removeNull($generalizedType);
16661690
}
16671691

16681692
$union = TypeCombinator::union(...$types);
@@ -1674,21 +1698,23 @@ private function inferPlusMinusTimesType(array $termTypes): Type
16741698
}
16751699

16761700
if ($driverType === DriverType::PDO_PGSQL) {
1677-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new FloatType(), $this->createNumericString(false)])) {
1701+
if ($this->containsOnlyNumericTypes($unionWithoutNull)) {
16781702
return $this->createNumericString($nullable);
16791703
}
16801704
}
16811705

16821706
if ($driverType === DriverType::SQLITE3 || $driverType === DriverType::PDO_SQLITE) {
1683-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new FloatType()])) {
1684-
return $this->createFloat($nullable);
1707+
if (!$this->containsOnlyNumericTypes(...$typesNoNull)) {
1708+
return new MixedType();
16851709
}
1686-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new StringType()])) {
1687-
return $this->createInteger($nullable);
1688-
}
1689-
if ($this->containsOnlyTypes($unionWithoutNull, [new FloatType(), new StringType()])) {
1690-
return $this->createFloat($nullable);
1710+
1711+
foreach ($typesNoNull as $typeNoNull) {
1712+
if ($typeNoNull->isFloat()->yes()) {
1713+
return $this->createFloat($nullable);
1714+
}
16911715
}
1716+
1717+
return $this->createFloatOrInt($nullable);
16921718
}
16931719

16941720
if ($driverType === DriverType::MYSQLI || $driverType === DriverType::PDO_MYSQL || $driverType === DriverType::PGSQL) {
@@ -1700,20 +1726,11 @@ private function inferPlusMinusTimesType(array $termTypes): Type
17001726
return $this->createNumericString($nullable);
17011727
}
17021728

1703-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new StringType()])) {
1704-
return $this->createFloat($nullable);
1705-
}
1706-
1707-
if ($this->containsOnlyTypes($unionWithoutNull, [new FloatType(), new StringType()])) {
1708-
return $this->createFloat($nullable);
1709-
}
1710-
1711-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new FloatType(), $this->createNumericString(false)])) {
1729+
if ($this->containsOnlyNumericTypes($unionWithoutNull)) {
17121730
return $this->createFloat($nullable);
17131731
}
17141732
}
17151733

1716-
// postgre fails and other drivers are unknown
17171734
return new MixedType();
17181735
}
17191736

@@ -1724,16 +1741,16 @@ private function inferDivisionType(array $termTypes): Type
17241741
{
17251742
// mysql sqlite pdo_pgsql pgsql
17261743
// col_float => float float string float
1727-
// col_decimal => string float string string
1744+
// col_decimal => string float|int string string
17281745
// col_int => int int int int
17291746
// col_bigint => int int int int
17301747
//
17311748
// col_int / col_int string int int int
17321749
// col_int / col_float float float string float
17331750
// col_float / col_float float float string float
17341751
// col_float / col_decimal float float string float
1735-
// col_int / col_decimal string float string string
1736-
// col_decimal / col_decimal string float string string
1752+
// col_int / col_decimal string float|int string string
1753+
// col_decimal / col_decimal string float|int string string
17371754
// col_string / col_string null null x x
17381755
// col_int / col_string null null x x
17391756
// col_bool / col_bool string int x x
@@ -1745,9 +1762,12 @@ private function inferDivisionType(array $termTypes): Type
17451762

17461763
$driverType = DriverType::detect($this->em->getConnection());
17471764
$types = [];
1765+
$typesNoNull = [];
17481766

17491767
foreach ($termTypes as $termType) {
1750-
$types[] = $this->generalizeLiteralType($termType, false);
1768+
$generalizedType = $this->generalizeLiteralType($termType, false);
1769+
$types[] = $generalizedType;
1770+
$typesNoNull[] = TypeCombinator::removeNull($generalizedType);
17511771
}
17521772

17531773
$union = TypeCombinator::union(...$types);
@@ -1771,15 +1791,17 @@ private function inferDivisionType(array $termTypes): Type
17711791
}
17721792

17731793
if ($driverType === DriverType::SQLITE3 || $driverType === DriverType::PDO_SQLITE) {
1774-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new FloatType()])) {
1775-
return $this->createFloat($nullable);
1794+
if (!$this->containsOnlyNumericTypes(...$typesNoNull)) {
1795+
return new MixedType();
17761796
}
1777-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new StringType()])) {
1778-
return $this->createInteger(true);
1779-
}
1780-
if ($this->containsOnlyTypes($unionWithoutNull, [new FloatType(), new StringType()])) {
1781-
return $this->createFloat(true);
1797+
1798+
foreach ($typesNoNull as $typeNoNull) {
1799+
if ($typeNoNull->isFloat()->yes()) {
1800+
return $this->createFloat($nullable);
1801+
}
17821802
}
1803+
1804+
return $this->createFloatOrInt($nullable);
17831805
}
17841806

17851807
if ($driverType === DriverType::MYSQLI || $driverType === DriverType::PDO_MYSQL || $driverType === DriverType::PGSQL) {
@@ -1795,17 +1817,9 @@ private function inferDivisionType(array $termTypes): Type
17951817
return $this->createFloat($nullable);
17961818
}
17971819

1798-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new FloatType(), $this->createNumericString(false)])) {
1820+
if ($this->containsOnlyNumericTypes($unionWithoutNull)) {
17991821
return $this->createFloat($nullable);
18001822
}
1801-
1802-
if ($this->containsOnlyTypes($unionWithoutNull, [new IntegerType(), new StringType()])) {
1803-
return $this->createFloat(true);
1804-
}
1805-
1806-
if ($this->containsOnlyTypes($unionWithoutNull, [new FloatType(), new StringType()])) {
1807-
return $this->createFloat(true);
1808-
}
18091823
}
18101824

18111825
return new MixedType();

0 commit comments

Comments
 (0)