Skip to content

Commit

Permalink
Issue doctrine#3631 - fix for Double data type when using MySQLi with…
Browse files Browse the repository at this point in the history
… languages that do not use '.' for the decimal point
  • Loading branch information
mmucklo committed Aug 19, 2019
1 parent 9ff47e7 commit ae55fe2
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 2 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,6 +35,7 @@ class MysqliStatement implements IteratorAggregate, Statement
ParameterType::NULL => 's',
ParameterType::INTEGER => 'i',
ParameterType::LARGE_OBJECT => 'b',
ParameterType::DOUBLE => 's',
];

/** @var mysqli */
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/Doctrine/DBAL/Driver/PDOStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/DBAL/ParameterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
9 changes: 9 additions & 0 deletions lib/Doctrine/DBAL/Types/FloatType.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Doctrine\DBAL\Types;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;

class FloatType extends Type
Expand Down Expand Up @@ -29,4 +30,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value === null ? null : (float) $value;
}

/**
* {@inheritdoc}
*/
public function getBindingType()
{
return ParameterType::DOUBLE;
}
}
123 changes: 123 additions & 0 deletions tests/Doctrine/Tests/DBAL/Functional/Types/DoubleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\DBAL\Functional\Types;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Schema\Table;
use Doctrine\Tests\DbalFunctionalTestCase;
use function abs;
use function floatval;
use function is_int;
use function is_string;
use function microtime;
use function sprintf;

class DoubleTest extends DbalFunctionalTestCase
{
protected function setUp() : void
{
parent::setUp();

$table = new Table('double_table');
$table->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]
);
}
}
17 changes: 17 additions & 0 deletions tests/Doctrine/Tests/DBAL/Functional/Types/LanguageDoubleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\DBAL\Functional\Types;

use const LC_ALL;
use function setlocale;

class LanguageDoubleTest extends DoubleTest
{
protected function setUp() : void
{
setlocale(LC_ALL, 'de_DE.UTF-8', 'de_DE', 'de', 'ge');
parent::setUp();
}
}
9 changes: 9 additions & 0 deletions tests/travis/install-language-de.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash

set -ex

echo "Installing language pack de..."

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 6B05F25D762E3157
sudo apt -y -q update
sudo apt install language-pack-de

0 comments on commit ae55fe2

Please sign in to comment.