@@ -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