Skip to content

Commit

Permalink
Support VARCHAR binding #4263
Browse files Browse the repository at this point in the history
  • Loading branch information
gjdanis committed Sep 16, 2020
1 parent c70832a commit 3fc29f5
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 5 deletions.
10 changes: 10 additions & 0 deletions docs/en/reference/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ or ``null`` if no data is present.
This can lead to type inconsistencies when reverse engineering the
type from the database.

ascii
+++++

Similar to the ``string`` type but for binding non unicode data. This type
should be used with database vendors where a binding type mismatch
can trigger an implicit cast and lead to performance problems.

text
++++

Expand Down Expand Up @@ -583,6 +590,9 @@ Please also notice the mapping specific footnotes for additional information.
| | | | +----------------------------------------------------------+
| | | | | ``NCHAR(n)`` [4]_ |
+-------------------+---------------+--------------------------+---------+----------------------------------------------------------+
| **ascii** | ``string`` | **SQL Server** | | ``VARCHAR(n)`` |
| | | | | ``CHAR(n)`` |
+-------------------+---------------+--------------------------+---------+----------------------------------------------------------+
| **text** | ``string`` | **MySQL** | *all* | ``TINYTEXT`` [16]_ |
| | | | +----------------------------------------------------------+
| | | | | ``TEXT`` [17]_ |
Expand Down
1 change: 1 addition & 0 deletions src/Driver/Mysqli/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class Statement implements StatementInterface
{
/** @var string[] */
protected static $_paramTypeMap = [
ParameterType::ASCII_STRING => 's',
ParameterType::STRING => 's',
ParameterType::BINARY => 's',
ParameterType::BOOLEAN => 'i',
Expand Down
19 changes: 14 additions & 5 deletions src/Driver/PDO/SQLSrv/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,20 @@ public function __construct(PDOStatement $statement)
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
if (
($type === ParameterType::LARGE_OBJECT || $type === ParameterType::BINARY)
&& $driverOptions === null
) {
$driverOptions = PDO::SQLSRV_ENCODING_BINARY;
switch ($type) {
case ParameterType::LARGE_OBJECT:
case ParameterType::BINARY:
if ($driverOptions === null) {
$driverOptions = PDO::SQLSRV_ENCODING_BINARY;
}

break;

case ParameterType::ASCII_STRING:
$type = PDO::PARAM_STR;
$length = 0;
$driverOptions = PDO::SQLSRV_ENCODING_SYSTEM;
break;
}

return $this->statement->bindParam($param, $variable, $type, $length, $driverOptions);
Expand Down
1 change: 1 addition & 0 deletions src/Driver/PDO/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ final class Statement implements StatementInterface
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => PDO::PARAM_STR,
ParameterType::ASCII_STRING => PDO::PARAM_STR,
ParameterType::BINARY => PDO::PARAM_LOB,
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
Expand Down
8 changes: 8 additions & 0 deletions src/Driver/SQLSrv/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ private function prepare()
];
break;

case ParameterType::ASCII_STRING:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR),
];
break;

default:
$params[$column - 1] =& $variable;
break;
Expand Down
5 changes: 5 additions & 0 deletions src/ParameterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ final class ParameterType
*/
public const BINARY = 16;

/**
* Represents an ascii string data type
*/
public const ASCII_STRING = 17;

/**
* This class cannot be instantiated.
*
Expand Down
40 changes: 40 additions & 0 deletions src/Types/AsciiStringType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Types;

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

use function sprintf;

class AsciiStringType extends StringType
{
/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $column, AbstractPlatform $platform)
{
$length = $column['length'] ?? null;

if (! isset($column['fixed'])) {
return sprintf('VARCHAR(%d)', $length);
}

return sprintf('CHAR(%d)', $length);
}

/**
* {@inheritdoc}
*/
public function getBindingType()
{
return ParameterType::ASCII_STRING;
}

public function getName(): string
{
return Types::ASCII_STRING;
}
}
1 change: 1 addition & 0 deletions src/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ abstract class Type
*/
private const BUILTIN_TYPES_MAP = [
Types::ARRAY => ArrayType::class,
Types::ASCII_STRING => AsciiStringType::class,
Types::BIGINT => BigIntType::class,
Types::BINARY => BinaryType::class,
Types::BLOB => BlobType::class,
Expand Down
1 change: 1 addition & 0 deletions src/Types/Types.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
final class Types
{
public const ARRAY = 'array';
public const ASCII_STRING = 'ascii_string';
public const BIGINT = 'bigint';
public const BINARY = 'binary';
public const BLOB = 'blob';
Expand Down
32 changes: 32 additions & 0 deletions tests/Functional/Types/StringBindingTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Types;

use Doctrine\DBAL\Driver\SQLSrv\Driver;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Tests\FunctionalTestCase;

class StringBindingTest extends FunctionalTestCase
{
public function testAsciiStringBinding(): void
{
if (! $this->driverSupportsAsciiBinding()) {
self::markTestSkipped('Driver does not support ascii string binding');
}

$statement = $this->connection->prepare('SELECT sql_variant_property(?, \'BaseType\')');

$statement->bindValue(1, 'test', ParameterType::ASCII_STRING);
$results = $statement->execute()->fetchOne();

self::assertEquals('varchar', $results);
}

private function driverSupportsAsciiBinding(): bool
{
return $this->connection->getDriver() instanceof Driver
|| $this->connection->getDriver() instanceof \Doctrine\DBAL\Driver\PDO\SQLSrv\Driver;
}
}
53 changes: 53 additions & 0 deletions tests/Types/AsciiStringTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Types;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\AsciiStringType;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class AsciiStringTest extends TestCase
{
/** @var AbstractPlatform|MockObject */
private $platform;

/** @var AsciiStringType */
private $type;

protected function setUp(): void
{
$this->platform = $this->createMock(AbstractPlatform::class);
$this->type = new AsciiStringType();
}

public function testReturnsBindingType(): void
{
self::assertEquals($this->type->getBindingType(), ParameterType::ASCII_STRING);
}

/**
* @param array<string, mixed> $column
*
* @dataProvider sqlDeclarationDataProvider
*/
public function testReturnsSQLDeclaration(string $expectedSql, array $column): void
{
$declarationSql = $this->type->getSQLDeclaration($column, $this->platform);
self::assertEquals($expectedSql, $declarationSql);
}

/**
* @return array<int, array{string, array<string, mixed>}>
*/
public static function sqlDeclarationDataProvider(): array
{
return [
['VARCHAR(12)', ['length' => 12]],
['CHAR(12)', ['length' => 12, 'fixed' => true]],
];
}
}

0 comments on commit 3fc29f5

Please sign in to comment.