diff --git a/.travis.yml b/.travis.yml index e0d87665991..8269a7d2ab6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ before_install: before_script: - if [[ "$DB" == "mysql" || "$DB" == "mysqli" || "$DB" == *"mariadb"* ]]; then mysql < tests/travis/create-mysql-schema.sql; fi; + - tests/travis/install-language-de.sh install: - rm composer.lock diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php index 43d70853ef5..c4c18396c9c 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -18,6 +18,7 @@ use function feof; use function fread; use function get_resource_type; +use function gettype; use function is_array; use function is_int; use function is_resource; @@ -34,6 +35,7 @@ class MysqliStatement implements IteratorAggregate, Statement ParameterType::NULL => 's', ParameterType::INTEGER => 'i', ParameterType::LARGE_OBJECT => 'b', + ParameterType::DOUBLE => 's', ]; /** @var mysqli */ @@ -272,10 +274,17 @@ private function sendLongData($streams) private function bindUntypedValues(array $values) { $params = []; - $types = str_repeat('s', count($values)); - + $types = ''; foreach ($values as &$v) { $params[] =& $v; + // fix for issue #3631 - detect parameter types as they have to be bound differently + switch (gettype($v)) { + case 'boolean': + $types .= self::$_paramTypeMap[ParameterType::BOOLEAN]; + break; + default: + $types .= self::$_paramTypeMap[ParameterType::STRING]; + } } return $this->_stmt->bind_param($types, ...$params); diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php index 5be4f2c046d..facfa157c04 100644 --- a/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -26,6 +26,7 @@ class PDOStatement extends \PDOStatement implements Statement ParameterType::BINARY => PDO::PARAM_LOB, ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, ParameterType::BOOLEAN => PDO::PARAM_BOOL, + ParameterType::DOUBLE => PDO::PARAM_STR, ]; private const FETCH_MODE_MAP = [ diff --git a/lib/Doctrine/DBAL/ParameterType.php b/lib/Doctrine/DBAL/ParameterType.php index 422fee895ee..53ba350c7f8 100644 --- a/lib/Doctrine/DBAL/ParameterType.php +++ b/lib/Doctrine/DBAL/ParameterType.php @@ -49,6 +49,11 @@ final class ParameterType */ public const BINARY = 16; + /** + * Represents a double data type. + */ + public const DOUBLE = 17; + /** * This class cannot be instantiated. */ diff --git a/lib/Doctrine/DBAL/Types/FloatType.php b/lib/Doctrine/DBAL/Types/FloatType.php index 4988d7253dc..f11a0a04f08 100644 --- a/lib/Doctrine/DBAL/Types/FloatType.php +++ b/lib/Doctrine/DBAL/Types/FloatType.php @@ -2,6 +2,7 @@ namespace Doctrine\DBAL\Types; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; class FloatType extends Type @@ -29,4 +30,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (float) $value; } + + /** + * {@inheritdoc} + */ + public function getBindingType() + { + return ParameterType::DOUBLE; + } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Types/DoubleTest.php b/tests/Doctrine/Tests/DBAL/Functional/Types/DoubleTest.php new file mode 100644 index 00000000000..4ad7c4ed99f --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Types/DoubleTest.php @@ -0,0 +1,123 @@ +addColumn('id', 'integer'); + $table->addColumn('val', 'float'); + $table->setPrimaryKey(['id']); + + $sm = $this->connection->getSchemaManager(); + $sm->dropAndCreateTable($table); + } + + public function testInsertAndSelect() : void + { + $value1 = 1.1; + $value2 = 77.99999999999; + $value3 = microtime(true); + + $this->insert(1, $value1); + $this->insert(2, $value2); + $this->insert(3, $value3); + + $result1 = $this->select(1); + $result2 = $this->select(2); + $result3 = $this->select(3); + + if (is_string($result1)) { + $result1 = floatval($result1); + $result2 = floatval($result2); + $result3 = floatval($result3); + } + + if ($result1 === false) { + $this->fail('Expected $result1 to not be false'); + + return; + } + if ($result2 === false) { + $this->fail('Expected $result2 to not be false'); + + return; + } + if ($result3 === false) { + $this->fail('Expected $result3 to not be false'); + + return; + } + + $diff1 = abs($result1 - $value1); + $diff2 = abs($result2 - $value2); + $diff3 = abs($result3 - $value3); + + $this->assertLessThanOrEqual(0.0001, $diff1, sprintf('%f, %f, %f', $diff1, $result1, $value1)); + $this->assertLessThanOrEqual(0.0001, $diff2, sprintf('%f, %f, %f', $diff2, $result2, $value2)); + $this->assertLessThanOrEqual(0.0001, $diff3, sprintf('%f, %f, %f', $diff3, $result3, $value3)); + + $result1 = $this->selectDouble($value1); + $result2 = $this->selectDouble($value2); + $result3 = $this->selectDouble($value3); + + $this->assertSame(is_int($result1) ? 1 : '1', $result1); + $this->assertSame(is_int($result2) ? 2 : '2', $result2); + $this->assertSame(is_int($result3) ? 3 : '3', $result3); + } + + private function insert(int $id, float $value) : void + { + $result = $this->connection->insert('double_table', [ + 'id' => $id, + 'val' => $value, + ], [ + ParameterType::INTEGER, + ParameterType::DOUBLE, + ]); + + self::assertSame(1, $result); + } + + /** + * @return mixed + */ + private function select(int $id) + { + return $this->connection->fetchColumn( + 'SELECT val FROM double_table WHERE id = ?', + [$id], + 0, + [ParameterType::INTEGER] + ); + } + + /** + * @return mixed + */ + private function selectDouble(float $value) + { + return $this->connection->fetchColumn( + 'SELECT id FROM double_table WHERE val = ?', + [$value], + 0, + [ParameterType::DOUBLE] + ); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Types/LanguageDoubleTest.php b/tests/Doctrine/Tests/DBAL/Functional/Types/LanguageDoubleTest.php new file mode 100644 index 00000000000..bbcced29843 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Types/LanguageDoubleTest.php @@ -0,0 +1,17 @@ +