From 3fc29f597ab72972ee5d87ba070c773e75da2019 Mon Sep 17 00:00:00 2001 From: Joe Danis Date: Mon, 14 Sep 2020 10:16:29 -0400 Subject: [PATCH] Support VARCHAR binding #4263 --- docs/en/reference/types.rst | 10 ++++ src/Driver/Mysqli/Statement.php | 1 + src/Driver/PDO/SQLSrv/Statement.php | 19 +++++-- src/Driver/PDO/Statement.php | 1 + src/Driver/SQLSrv/Statement.php | 8 +++ src/ParameterType.php | 5 ++ src/Types/AsciiStringType.php | 40 +++++++++++++++ src/Types/Type.php | 1 + src/Types/Types.php | 1 + tests/Functional/Types/StringBindingTest.php | 32 ++++++++++++ tests/Types/AsciiStringTest.php | 53 ++++++++++++++++++++ 11 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 src/Types/AsciiStringType.php create mode 100644 tests/Functional/Types/StringBindingTest.php create mode 100644 tests/Types/AsciiStringTest.php diff --git a/docs/en/reference/types.rst b/docs/en/reference/types.rst index c504002c300..24b8a470839 100644 --- a/docs/en/reference/types.rst +++ b/docs/en/reference/types.rst @@ -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 ++++ @@ -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]_ | diff --git a/src/Driver/Mysqli/Statement.php b/src/Driver/Mysqli/Statement.php index 657a4e56e3c..92795077fbd 100644 --- a/src/Driver/Mysqli/Statement.php +++ b/src/Driver/Mysqli/Statement.php @@ -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', diff --git a/src/Driver/PDO/SQLSrv/Statement.php b/src/Driver/PDO/SQLSrv/Statement.php index 12d956bb950..ee01c64d266 100644 --- a/src/Driver/PDO/SQLSrv/Statement.php +++ b/src/Driver/PDO/SQLSrv/Statement.php @@ -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); diff --git a/src/Driver/PDO/Statement.php b/src/Driver/PDO/Statement.php index 550984c706a..642ad2b1db1 100644 --- a/src/Driver/PDO/Statement.php +++ b/src/Driver/PDO/Statement.php @@ -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, diff --git a/src/Driver/SQLSrv/Statement.php b/src/Driver/SQLSrv/Statement.php index fc482ab42f0..298d2271b1a 100644 --- a/src/Driver/SQLSrv/Statement.php +++ b/src/Driver/SQLSrv/Statement.php @@ -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; diff --git a/src/ParameterType.php b/src/ParameterType.php index 7834c88afff..147b3cd3c76 100644 --- a/src/ParameterType.php +++ b/src/ParameterType.php @@ -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. * diff --git a/src/Types/AsciiStringType.php b/src/Types/AsciiStringType.php new file mode 100644 index 00000000000..ca6e3bd1379 --- /dev/null +++ b/src/Types/AsciiStringType.php @@ -0,0 +1,40 @@ + ArrayType::class, + Types::ASCII_STRING => AsciiStringType::class, Types::BIGINT => BigIntType::class, Types::BINARY => BinaryType::class, Types::BLOB => BlobType::class, diff --git a/src/Types/Types.php b/src/Types/Types.php index 771a01780ba..56bf3f51cf0 100644 --- a/src/Types/Types.php +++ b/src/Types/Types.php @@ -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'; diff --git a/tests/Functional/Types/StringBindingTest.php b/tests/Functional/Types/StringBindingTest.php new file mode 100644 index 00000000000..25d733cc4d9 --- /dev/null +++ b/tests/Functional/Types/StringBindingTest.php @@ -0,0 +1,32 @@ +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; + } +} diff --git a/tests/Types/AsciiStringTest.php b/tests/Types/AsciiStringTest.php new file mode 100644 index 00000000000..7c6aa7f2e8e --- /dev/null +++ b/tests/Types/AsciiStringTest.php @@ -0,0 +1,53 @@ +platform = $this->createMock(AbstractPlatform::class); + $this->type = new AsciiStringType(); + } + + public function testReturnsBindingType(): void + { + self::assertEquals($this->type->getBindingType(), ParameterType::ASCII_STRING); + } + + /** + * @param array $column + * + * @dataProvider sqlDeclarationDataProvider + */ + public function testReturnsSQLDeclaration(string $expectedSql, array $column): void + { + $declarationSql = $this->type->getSQLDeclaration($column, $this->platform); + self::assertEquals($expectedSql, $declarationSql); + } + + /** + * @return array}> + */ + public static function sqlDeclarationDataProvider(): array + { + return [ + ['VARCHAR(12)', ['length' => 12]], + ['CHAR(12)', ['length' => 12, 'fixed' => true]], + ]; + } +}