Skip to content

Commit

Permalink
Issue #3631 - fix for Double data type when using MySQLi with languag…
Browse files Browse the repository at this point in the history
…es that do not use '.' for the decimal point
  • Loading branch information
mmucklo committed Aug 18, 2019
1 parent 9ff47e7 commit dc71600
Show file tree
Hide file tree
Showing 8 changed files with 174 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 => 'd',
];

/** @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]
);
}
}
15 changes: 15 additions & 0 deletions tests/Doctrine/Tests/DBAL/Functional/Types/LanguageDoubleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\DBAL\Functional\Types;

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 dc71600

Please sign in to comment.