diff --git a/.scrutinizer.yml b/.scrutinizer.yml index f51cf15b63f..e03fc0f0cf8 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -3,7 +3,7 @@ build: analysis: environment: php: - version: 7.1 + version: 7.2 cache: disabled: false directories: @@ -21,7 +21,7 @@ before_commands: tools: external_code_coverage: timeout: 3600 - runs: 27 # 23x Travis (jobs with COVERAGE=yes) + 3x AppVeyor (jobs with coverage=yes) + 1x ContinuousPHP + runs: 26 # 22x Travis (jobs with COVERAGE=yes) + 3x AppVeyor (jobs with coverage=yes) + 1x ContinuousPHP filter: excluded_paths: diff --git a/.travis.yml b/.travis.yml index de5c5dcd9b8..297d9caf726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ cache: - $HOME/.composer/cache php: - - 7.1 - 7.2 - 7.3 - nightly @@ -57,11 +56,11 @@ jobs: - env: DB=pgsql POSTGRESQL_VERSION=11.0 exclude: - - php: 7.1 + - php: 7.2 env: DB=sqlite - - php: 7.1 + - php: 7.2 env: DB=mysql - - php: 7.1 + - php: 7.2 env: DB=mysqli - php: 7.2 env: DB=sqlite @@ -79,10 +78,6 @@ jobs: env: DB=mysqli include: - - stage: Test - php: 7.1 - env: DB=sqlite - - stage: Test php: 7.2 env: DB=sqlite COVERAGE=yes @@ -95,12 +90,6 @@ jobs: php: 7.2 env: DB=mysqli COVERAGE=yes - - stage: Test - php: 7.1 - env: DB=mysql MYSQL_VERSION=5.7 - sudo: required - before_script: - - bash ./tests/travis/install-mysql-5.7.sh - stage: Test php: 7.2 env: DB=mysql MYSQL_VERSION=5.7 COVERAGE=yes @@ -120,12 +109,6 @@ jobs: before_script: - bash ./tests/travis/install-mysql-5.7.sh - - stage: Test - php: 7.1 - env: DB=mysqli MYSQL_VERSION=5.7 - sudo: required - before_script: - - bash ./tests/travis/install-mysql-5.7.sh - stage: Test php: 7.2 env: DB=mysqli MYSQL_VERSION=5.7 COVERAGE=yes @@ -181,12 +164,6 @@ jobs: addons: mariadb: 10.2 - - stage: Test - php: 7.1 - env: DB=mariadb MARIADB_VERSION=10.3 - addons: - mariadb: 10.3 - - stage: Test php: 7.2 env: DB=mariadb MARIADB_VERSION=10.3 COVERAGE=yes @@ -205,12 +182,6 @@ jobs: addons: mariadb: 10.3 - - stage: Test - php: 7.1 - env: DB=mariadb.mysqli MARIADB_VERSION=10.3 - addons: - mariadb: 10.3 - - stage: Test php: 7.2 env: DB=mariadb.mysqli MARIADB_VERSION=10.3 COVERAGE=yes @@ -229,14 +200,6 @@ jobs: addons: mariadb: 10.3 - - stage: Test - php: 7.2 - env: DB=pgsql POSTGRESQL_VERSION=9.2 COVERAGE=yes - services: - - postgresql - addons: - postgresql: "9.2" - - stage: Test php: 7.2 env: DB=pgsql POSTGRESQL_VERSION=9.3 COVERAGE=yes @@ -280,14 +243,6 @@ jobs: before_script: - bash ./tests/travis/install-postgres-10.sh - - stage: Test - php: 7.1 - env: DB=pgsql POSTGRESQL_VERSION=11.0 - sudo: required - services: - - docker - before_script: - - bash ./tests/travis/install-postgres-11.sh - stage: Test php: 7.2 env: DB=pgsql POSTGRESQL_VERSION=11.0 COVERAGE=yes @@ -313,15 +268,6 @@ jobs: before_script: - bash ./tests/travis/install-postgres-11.sh - - stage: Test - php: 7.1 - env: DB=sqlsrv - sudo: required - services: - - docker - before_script: - - bash ./tests/travis/install-mssql-$DB.sh - - bash ./tests/travis/install-mssql.sh - stage: Test php: 7.2 env: DB=sqlsrv COVERAGE=yes @@ -350,15 +296,6 @@ jobs: - bash ./tests/travis/install-mssql-$DB.sh - bash ./tests/travis/install-mssql.sh - - stage: Test - php: 7.1 - env: DB=pdo_sqlsrv - sudo: required - services: - - docker - before_script: - - bash ./tests/travis/install-mssql-$DB.sh - - bash ./tests/travis/install-mssql.sh - stage: Test php: 7.2 env: DB=pdo_sqlsrv COVERAGE=yes @@ -387,15 +324,6 @@ jobs: - bash ./tests/travis/install-mssql-$DB.sh - bash ./tests/travis/install-mssql.sh - - stage: Test - php: 7.1 - env: DB=ibm_db2 - sudo: required - services: - - docker - before_script: - - bash ./tests/travis/install-db2.sh - - bash ./tests/travis/install-db2-$DB.sh - stage: Test php: 7.2 env: DB=ibm_db2 COVERAGE=yes @@ -406,12 +334,6 @@ jobs: - bash ./tests/travis/install-db2.sh - bash ./tests/travis/install-db2-$DB.sh - - stage: Test - php: 7.1 - env: DB=sqlite DEPENDENCIES=low - install: - - travis_retry composer update --prefer-dist --prefer-lowest - - stage: Test if: type = cron php: 7.2 diff --git a/UPGRADE.md b/UPGRADE.md index bf079383657..e5402dc492f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,139 @@ +# Upgrade to 3.0 + +## BC BREAK: `Doctrine\DBAL\Types\Type::getDefaultLength()` removed + +The `Doctrine\DBAL\Types\Type::getDefaultLength()` method has been removed as it served no purpose. + +## BC BREAK: `Doctrine\DBAL\Types\Type::__toString()` removed + +Relying on string representation was discouraged and has been removed. + +## BC BREAK: The `NULL` value of `$offset` in LIMIT queries is not allowed + +The `NULL` value of the `$offset` argument in `AbstractPlatform::(do)?ModifyLimitQuery()` methods is no longer allowed. The absence of the offset should be indicated with a `0` which is now the default value. + +## BC BREAK: Removed dbal:import CLI command + +The `dbal:import` CLI command has been removed since it only worked with PDO-based drivers by relying on a non-documented behavior of the extension, and it was impossible to make it work with other drivers. +Please use other database client applications for import, e.g.: + + * For MySQL and MariaDB: `mysql [dbname] < data.sql`. + * For PostgreSQL: `psql [dbname] < data.sql`. + * For SQLite: `sqlite3 /path/to/file.db < data.sql`. + +## BC BREAK: Removed support for DB-generated UUIDs + +The support for DB-generated UUIDs was removed as non-portable. +Please generate UUIDs on the application side (e.g. using [ramsey/uuid](https://packagist.org/packages/ramsey/uuid)). + +## BC BREAK: Removed MsSQLKeywords class + +The `Doctrine\DBAL\Platforms\MsSQLKeywords` has been removed. +Please use `Doctrine\DBAL\Platforms\SQLServerPlatform `instead. + +## BC BREAK: Removed PDO DB2 driver + +This PDO-based IBM DB2 driver (built on top of pdo_ibm extension) has already been unsupported as of 2.5, it has now been now removed. + +The following class has been removed: + + * `Doctrine\DBAL\Driver\PDOIbm\Driver` + +## BC BREAK: Removed support for SQL Anywhere 12 and older + +DBAL now requires SQL Anywhere 16 or newer, support for unmaintained versions has been dropped. +If you are using any of the legacy versions, you have to upgrade to newer SQL Anywhere version (16+). +`Doctrine\DBAL\Platforms\SQLAnywherePlatform` and `Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords` now represent the SQL Anywhere 16. + +The following classes have been removed: + + * `Doctrine\DBAL\Platforms\SQLAnywhere11Platform` + * `Doctrine\DBAL\Platforms\SQLAnywhere12Platform` + * `Doctrine\DBAL\Platforms\SQLAnywhere16Platform` + * `Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords` + * `Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords` + * `Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords` + +## BC BREAK: Removed support for SQL Server 2005 and older + +DBAL now requires SQL Server 2008 or newer, support for unmaintained versions has been dropped. +If you are using any of the legacy versions, you have to upgrade to newer SQL Server version (2012+ is recommended). +`Doctrine\DBAL\Platforms\SQLServerPlatform` and `Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords` now represent the SQL Server 2008. + +The following classes have been removed: + + * `Doctrine\DBAL\Platforms\SQLServer2005Platform` + * `Doctrine\DBAL\Platforms\SQLServer2008Platform` + * `Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords` + * `Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords` + +## BC BREAK: Removed support for PostgreSQL 9.2 and older + +DBAL now requires PostgeSQL 9.3 or newer, support for unmaintained versions has been dropped. +If you are using any of the legacy versions, you have to upgrade to newer PostgreSQL version (9.6+ is recommended). +`Doctrine\DBAL\Platforms\PostgreSqlPlatform` and `Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords` now represent the PostgreSQL 9.3. + +The following classes have been removed: + + * `Doctrine\DBAL\Platforms\PostgreSQL91Platform` + * `Doctrine\DBAL\Platforms\PostgreSQL92Platform` + * `Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords` + * `Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords` + +## BC BREAK: Removed Doctrine\DBAL\Version + +The Doctrine\DBAL\Version class is no longer available: please refrain from checking the DBAL version at runtime. + +## BC BREAK: the PDO symbols are no longer part of the DBAL API + +1. The support of `PDO::PARAM_*`, `PDO::FETCH_*`, `PDO::CASE_*` and `PDO::PARAM_INPUT_OUTPUT` constants in the DBAL API is removed. +2. `\Doctrine\DBAL\Driver\PDOConnection` does not extend `\PDO` anymore. Please use `\Doctrine\DBAL\Driver\PDOConnection::getWrappedConnection()` to access the underlying `PDO` object. +3. `\Doctrine\DBAL\Driver\PDOStatement` does not extend `\PDOStatement` anymore. + +Before: + + use Doctrine\DBAL\Portability\Connection; + + $params = array( + 'wrapperClass' => Connection::class, + 'fetch_case' => PDO::CASE_LOWER, + ); + + $stmt->bindValue(1, 1, PDO::PARAM_INT); + $stmt->fetchAll(PDO::FETCH_COLUMN); + +After: + + use Doctrine\DBAL\ColumnCase; + use Doctrine\DBAL\FetchMode; + use Doctrine\DBAL\ParameterType; + use Doctrine\DBAL\Portability\Connection; + + $params = array( + 'wrapperClass' => Connection::class, + 'fetch_case' => ColumnCase::LOWER, + ); + + $stmt->bindValue(1, 1, ParameterType::INTEGER); + $stmt->fetchAll(FetchMode::COLUMN); + +## BC BREAK: Removed Drizzle support + +The Drizzle project is abandoned and is therefore not supported by Doctrine DBAL anymore. + +## BC BREAK: SQLLogger changes + +- The `SQLLogger` interface has changed; the methods are the same but use scalar type hints, return types, and non-nullable arrays. +- `SQLLogger` implementations: `DebugStack`, `EchoSQLLogger`, `LoggerChain` are now final. +- `Configuration::getSQLLogger()` does not return `null` anymore, but a `NullLogger` implementation. +- `Configuration::setSQLLogger()` does not allow `null` anymore. + +## BC BREAK: Changes to handling binary fields + +- Binary fields whose length exceeds the maximum field size on a given platform are no longer represented as `BLOB`s. + Use binary fields of a size which fits all target platforms, or use blob explicitly instead. +- Binary fields are no longer represented as streams in PHP. They are represented as strings. + # Upgrade to 2.9 ## Deprecated `Configuration::getFilterSchemaAssetsExpression()`, `::setFilterSchemaAssetsExpression()` and `AbstractSchemaManager::getFilterSchemaAssetsExpression()`. diff --git a/build.properties b/build.properties index 309e0e2105c..e69de29bb2d 100644 --- a/build.properties +++ b/build.properties @@ -1,3 +0,0 @@ -# Version class and file -project.version_class = Doctrine\\DBAL\\Version -project.version_file = lib/Doctrine/DBAL/Version.php diff --git a/build.xml b/build.xml index 064a6084e7e..95f80d42398 100644 --- a/build.xml +++ b/build.xml @@ -38,29 +38,6 @@ - - - - - - - - - - - - - - - - - diff --git a/composer.json b/composer.json index 9d5f610c2b8..27c6a6c5816 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,10 @@ {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} ], "require": { - "php": "^7.1", - "ext-pdo": "*", + "php": "^7.2", "doctrine/cache": "^1.0", - "doctrine/event-manager": "^1.0" + "doctrine/event-manager": "^1.0", + "ocramius/package-versions": "^1.2" }, "require-dev": { "doctrine/coding-standard": "^5.0", diff --git a/composer.lock b/composer.lock index 0c15627967d..7a658c5c312 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1147a9427c03f98ca063806250633645", + "content-hash": "aa8c419d7e09c6754e7f58789037136f", "packages": [ { "name": "doctrine/cache", @@ -153,6 +153,55 @@ "eventmanager" ], "time": "2018-06-11T11:59:03+00:00" + }, + { + "name": "ocramius/package-versions", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/Ocramius/PackageVersions.git", + "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/4489d5002c49d55576fa0ba786f42dbb009be46f", + "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0.0", + "php": "^7.1.0" + }, + "require-dev": { + "composer/composer": "^1.6.3", + "ext-zip": "*", + "infection/infection": "^0.7.1", + "phpunit/phpunit": "^7.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "time": "2018-02-05T13:05:30+00:00" } ], "packages-dev": [ @@ -1048,55 +1097,6 @@ ], "time": "2018-06-03T11:33:10+00:00" }, - { - "name": "ocramius/package-versions", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/Ocramius/PackageVersions.git", - "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/4489d5002c49d55576fa0ba786f42dbb009be46f", - "reference": "4489d5002c49d55576fa0ba786f42dbb009be46f", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0.0", - "php": "^7.1.0" - }, - "require-dev": { - "composer/composer": "^1.6.3", - "ext-zip": "*", - "infection/infection": "^0.7.1", - "phpunit/phpunit": "^7.0.0" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "time": "2018-02-05T13:05:30+00:00" - }, { "name": "phar-io/manifest", "version": "1.0.3", @@ -2959,8 +2959,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^7.1", - "ext-pdo": "*" + "php": "^7.2" }, "platform-dev": [] } diff --git a/docs/en/reference/configuration.rst b/docs/en/reference/configuration.rst index 4fa8991ec33..a4fba806a0c 100644 --- a/docs/en/reference/configuration.rst +++ b/docs/en/reference/configuration.rst @@ -73,12 +73,8 @@ full driver name:: pdo-mysql://localhost:4486/foo?charset=UTF-8 -If you wanted to use the ``drizzle_pdo__mysql`` driver instead:: - - drizzle-pdo-mysql://localhost:4486/foo?charset=UTF-8 - -In the last two examples above, mind the dashes instead of the -underscores in the URL schemes. +In the example above, mind the dashes instead of the +underscores in the URL scheme. For connecting to an SQLite database, the authority portion of the URL is obviously irrelevant and thus can be omitted. The path part @@ -128,8 +124,6 @@ interfaces to use. It can be configured in one of three ways: - ``pdo_mysql``: A MySQL driver that uses the pdo_mysql PDO extension. - - ``drizzle_pdo_mysql``: A Drizzle driver that uses pdo_mysql PDO - extension. - ``mysqli``: A MySQL driver that uses the mysqli extension. - ``pdo_sqlite``: An SQLite driver that uses the pdo_sqlite PDO extension. @@ -197,23 +191,6 @@ pdo_mysql - ``charset`` (string): The charset used when connecting to the database. -drizzle_pdo_mysql -^^^^^^^^^^^^^^^^^ - -**Requires** drizzle plugin ``mysql_protocol`` or ``mysql_unix_socket_protocol`` to be enabled. -On Ubuntu this can be done by editing ``/etc/drizzle/conf.d/mysql-protocol.cnf`` -or ``/etc/drizzle/conf.d/mysql-unix-socket-protocol.cnf`` and restarting the drizzled daemon. - -- ``user`` (string): Username to use when connecting to the - database. Only needed if authentication is configured for drizzled. -- ``password`` (string): Password to use when connecting to the - database. Only needed if authentication is configured for drizzled. -- ``host`` (string): Hostname of the database to connect to. -- ``port`` (integer): Port of the database to connect to. -- ``dbname`` (string): Name of the database/schema to connect to. -- ``unix_socket`` (string): Name of the socket used to connect to - the database. - mysqli ^^^^^^ @@ -252,20 +229,20 @@ pdo_pgsql - ``sslmode`` (string): Determines whether or with what priority a SSL TCP/IP connection will be negotiated with the server. See the list of available modes: - `http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLMODE` + `https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNECT-SSLMODE` - ``sslrootcert`` (string): specifies the name of a file containing SSL certificate authority (CA) certificate(s). If the file exists, the server's certificate will be verified to be signed by one of these authorities. - See http://www.postgresql.org/docs/9.0/static/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT + See https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT - ``sslcert`` (string): specifies the file name of the client SSL certificate. - See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLCERT` + See `https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNECT-SSLCERT` - ``sslkey`` (string): specifies the location for the secret key used for the client certificate. - See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLKEY` + See `https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNECT-SSLKEY` - ``sslcrl`` (string): specifies the file name of the SSL certificate revocation list (CRL). - See `https://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-CONNECT-SSLCRL` + See `https://www.postgresql.org/docs/9.3/static/libpq-connect.html#LIBPQ-CONNECT-SSLCRL` - ``application_name`` (string): Name of the application that is connecting to database. Optional. It will be displayed at ``pg_stat_activity``. @@ -333,14 +310,9 @@ sqlanywhere Depending on the used underlying platform version, you can specify any other connection parameter that is supported by the particular platform version via the ``driverOptions`` option. -You can find a list of supported connection parameters for each -platform version here: - -- `SQL Anywhere 10.0.1 `_ -- `SQL Anywhere 11.0.0 `_ -- `SQL Anywhere 11.0.1 `_ -- `SQL Anywhere 12.0.0 `_ -- `SQL Anywhere 12.0.1 `_ +You can find a list of supported connection parameters for the +currently supported platform here: + - `SAP Sybase SQL Anywhere 16.0 `_ Automatic platform version detection diff --git a/docs/en/reference/introduction.rst b/docs/en/reference/introduction.rst index ae495ea9815..5318303a7c6 100644 --- a/docs/en/reference/introduction.rst +++ b/docs/en/reference/introduction.rst @@ -21,7 +21,6 @@ The following database vendors are currently supported: - PostgreSQL - SAP Sybase SQL Anywhere - SQLite -- Drizzle The Doctrine 2 database layer can be used independently of the object-relational mapper. In order to use the DBAL all you need is diff --git a/docs/en/reference/platforms.rst b/docs/en/reference/platforms.rst index 32ef74304ea..12e292eec11 100644 --- a/docs/en/reference/platforms.rst +++ b/docs/en/reference/platforms.rst @@ -50,18 +50,15 @@ Oracle Microsoft SQL Server ^^^^^^^^^^^^^^^^^^^^ -- ``SQLServerPlatform`` for version 2000 and above. -- ``SQLServer2005Platform`` for version 2005 and above. -- ``SQLServer2008Platform`` for version 2008 and above. +- ``SQLServerPlatform`` for version 2008 and above. - ``SQLServer2012Platform`` for version 2012 and above. PostgreSQL ^^^^^^^^^^ - ``PostgreSqlPlatform`` for all versions. -- ``PostgreSQL91Platform`` for version 9.1 and above. -- ``PostgreSQL92Platform`` for version 9.2 and above. - ``PostgreSQL94Platform`` for version 9.4 and above. +- ``PostgreSQL100Platform`` for version 10.0 and above. SAP Sybase SQL Anywhere ^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,11 +73,6 @@ SQLite - ``SqlitePlatform`` for all versions. -Drizzle -^^^^^^ - -- ``DrizzlePlatform`` for all versions. - It is highly encouraged to use the platform class that matches your database vendor and version best. Otherwise it is not guaranteed that the compatibility in terms of SQL dialect and feature support diff --git a/docs/en/reference/schema-representation.rst b/docs/en/reference/schema-representation.rst index b250a5c5109..381a997e321 100644 --- a/docs/en/reference/schema-representation.rst +++ b/docs/en/reference/schema-representation.rst @@ -115,10 +115,10 @@ The following options are not completely portable but are supported by most of t vendors: - **unsigned** (boolean): Whether a ``smallint``, ``integer`` or ``bigint`` Doctrine - type column should allow unsigned values only. Supported by MySQL, SQL Anywhere - and Drizzle. Defaults to ``false``. + type column should allow unsigned values only. Supported by MySQL and SQL Anywhere. + Defaults to ``false``. - **comment** (integer|string): The column comment. Supported by MySQL, PostgreSQL, - Oracle, SQL Server, SQL Anywhere and Drizzle. Defaults to ``null``. + Oracle, SQL Server and SQL Anywhere. Defaults to ``null``. Vendor specific options ^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,8 +133,8 @@ The following options are completely vendor specific and absolutely not portable supported by some vendors but not portable: - **charset** (string): The character set to use for the column. Currently only supported - on MySQL and Drizzle. + on MySQL. - **collation** (string): The collation to use for the column. Supported by MySQL, PostgreSQL, - Sqlite, SQL Server and Drizzle. + Sqlite and SQL Server. - **check** (string): The check constraint clause to add to the column. Defaults to ``null``. diff --git a/docs/en/reference/types.rst b/docs/en/reference/types.rst index f4916a69672..dd733f33a51 100644 --- a/docs/en/reference/types.rst +++ b/docs/en/reference/types.rst @@ -517,8 +517,6 @@ Please also notice the mapping specific footnotes for additional information. +===================+===============+==========================+=========+==========================================================+ | **smallint** | ``integer`` | **MySQL** | *all* | ``SMALLINT`` ``UNSIGNED`` [10]_ ``AUTO_INCREMENT`` [11]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **Drizzle** | *all* | ``INT`` ``UNSIGNED`` [10]_ ``AUTO_INCREMENT`` [11]_ | -| | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``SMALLINT`` | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``NUMBER(5)`` | @@ -527,11 +525,9 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Anywhere** | *all* | ``UNSIGNED`` [10]_ ``SMALLINT`` ``IDENTITY`` [11]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQLite** | *all* | ``INTEGER`` [16]_ | +| | | **SQLite** | *all* | ``INTEGER`` [15]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **integer** | ``integer`` | **MySQL** | *all* | ``INT`` ``UNSIGNED`` [10]_ ``AUTO_INCREMENT`` [11]_ | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``INT`` [12]_ | | | | | +----------------------------------------------------------+ @@ -543,12 +539,10 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Anywhere** | *all* | ``UNSIGNED`` [10]_ ``INT`` ``IDENTITY`` [11]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQLite** | *all* | ``INTEGER`` [16]_ | +| | | **SQLite** | *all* | ``INTEGER`` [15]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **bigint** | ``string`` | **MySQL** | *all* | ``BIGINT`` ``UNSIGNED`` [10]_ ``AUTO_INCREMENT`` [11]_ | -| | [8]_ +--------------------------+ | | -| | | **Drizzle** | | | -| | +--------------------------+---------+----------------------------------------------------------+ +| | [8]_ +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``BIGINT`` [12]_ | | | | | +----------------------------------------------------------+ | | | | | ``BIGSERIAL`` [11]_ | @@ -559,7 +553,7 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Anywhere** | *all* | ``UNSIGNED`` [10]_ ``BIGINT`` ``IDENTITY`` [11]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQLite** | *all* | ``INTEGER`` [16]_ | +| | | **SQLite** | *all* | ``INTEGER`` [15]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **decimal** [7]_ | ``string`` | **MySQL** | *all* | ``NUMERIC(p, s)`` ``UNSIGNED`` [10]_ | | | [9]_ +--------------------------+---------+----------------------------------------------------------+ @@ -572,8 +566,6 @@ Please also notice the mapping specific footnotes for additional information. | | | **SQL Anywhere** | | | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **float** | ``float`` | **MySQL** | *all* | ``DOUBLE PRECISION`` ``UNSIGNED`` [10]_ | | | +--------------------------+---------+----------------------------------------------------------+ @@ -586,8 +578,6 @@ Please also notice the mapping specific footnotes for additional information. | | | **SQL Anywhere** | | | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **string** | ``string`` | **MySQL** | *all* | ``VARCHAR(n)`` [3]_ | | [2]_ [5]_ | +--------------------------+ | | @@ -597,8 +587,6 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+ | | | | | **SQLite** | | | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **Drizzle** | *all* | ``VARCHAR(n)`` | -| | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``VARCHAR2(n)`` [3]_ | | | | | +----------------------------------------------------------+ | | | | | ``CHAR(n)`` [4]_ | @@ -607,19 +595,17 @@ Please also notice the mapping specific footnotes for additional information. | | | | +----------------------------------------------------------+ | | | | | ``NCHAR(n)`` [4]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **text** | ``string`` | **MySQL** | *all* | ``TINYTEXT`` [17]_ | +| **text** | ``string`` | **MySQL** | *all* | ``TINYTEXT`` [16]_ | | | | | +----------------------------------------------------------+ -| | | | | ``TEXT`` [18]_ | +| | | | | ``TEXT`` [17]_ | | | | | +----------------------------------------------------------+ -| | | | | ``MEDIUMTEXT`` [19]_ | +| | | | | ``MEDIUMTEXT`` [18]_ | | | | | +----------------------------------------------------------+ -| | | | | ``LONGTEXT`` [20]_ | +| | | | | ``LONGTEXT`` [19]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``TEXT`` | | | +--------------------------+ | | | | | **SQL Anywhere** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``CLOB`` | | | +--------------------------+ | | @@ -632,8 +618,6 @@ Please also notice the mapping specific footnotes for additional information. | | | **Oracle** | | | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``UNIQUEIDENTIFIER`` | | | +--------------------------+ | | @@ -647,27 +631,23 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+ | ``BINARY(n)`` [4]_ | | | | **SQL Anywhere** | | | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **Drizzle** | *all* | ``VARBINARY(n)`` | -| | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``RAW(n)`` | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **PostgreSQL** | *all* | ``BYTEA`` [16]_ | +| | | **PostgreSQL** | *all* | ``BYTEA`` [15]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQLite** | *all* | ``BLOB`` [16]_ | +| | | **SQLite** | *all* | ``BLOB`` [15]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **blob** | ``resource`` | **MySQL** | *all* | ``TINYBLOB`` [17]_ | +| **blob** | ``resource`` | **MySQL** | *all* | ``TINYBLOB`` [16]_ | | | | | +----------------------------------------------------------+ -| | | | | ``BLOB`` [18]_ | +| | | | | ``BLOB`` [17]_ | | | | | +----------------------------------------------------------+ -| | | | | ``MEDIUMBLOB`` [19]_ | +| | | | | ``MEDIUMBLOB`` [18]_ | | | | | +----------------------------------------------------------+ -| | | | | ``LONGBLOB`` [20]_ | +| | | | | ``LONGBLOB`` [19]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``BLOB`` | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``VARBINARY(MAX)`` | | | +--------------------------+---------+----------------------------------------------------------+ @@ -680,8 +660,6 @@ Please also notice the mapping specific footnotes for additional information. | | | **PostgreSQL** | *all* | ``BOOLEAN`` | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``BIT`` | | | +--------------------------+ | | @@ -698,16 +676,10 @@ Please also notice the mapping specific footnotes for additional information. | | | **SQL Anywhere** | | | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+ | -| | | **SQL Server** | >= 2008 | | -| | | +---------+----------------------------------------------------------+ -| | | | < 2008 | ``DATETIME`` [16]_ | +| | | **SQL Server** | "all" | | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **datetime** | ``\DateTime`` | **MySQL** | *all* | ``DATETIME`` [13]_ | -| | +--------------------------+ +----------------------------------------------------------+ -| | | **Drizzle** | | ``TIMESTAMP`` [14]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``DATETIME`` | | | +--------------------------+ | | @@ -719,53 +691,41 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``TIMESTAMP(0)`` | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **datetimetz** | ``\DateTime`` | **MySQL** | *all* | ``DATETIME`` [15]_ [16]_ | -| | +--------------------------+ | | -| | | **Drizzle** | | | +| **datetimetz** | ``\DateTime`` | **MySQL** | *all* | ``DATETIME`` [14]_ [15]_ | | | +--------------------------+ | | | | | **SQLite** | | | | | +--------------------------+---------+ | -| | | **SQL Server** | < 2008 | | -| | | +---------+----------------------------------------------------------+ -| | | | >= 2008 | ``DATETIMEOFFSET(6)`` | +| | | **SQL Server** | "all" | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``TIMESTAMP(0) WITH TIME ZONE`` | | | +--------------------------+ | | | | | **Oracle** | | | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQL Anywhere** | < 12 | ``DATETIME`` [15]_ [16]_ | -| | | +---------+----------------------------------------------------------+ -| | | | >= 12 | ``TIMESTAMP WITH TIME ZONE`` | +| | | **SQL Anywhere** | "all" | ``TIMESTAMP WITH TIME ZONE`` | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ | **time** | ``\DateTime`` | **MySQL** | *all* | ``TIME`` | | | +--------------------------+ | | | | | **SQL Anywhere** | | | | | +--------------------------+ | | | | | **SQLite** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``TIME(0) WITHOUT TIME ZONE`` | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **Oracle** | *all* | ``DATE`` [16]_ | +| | | **Oracle** | *all* | ``DATE`` [15]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **SQL Server** | < 2008 | ``DATETIME`` [16]_ | -| | | +---------+----------------------------------------------------------+ -| | | | >= 2008 | ``TIME(0)`` | +| | | **SQL Server** | "all" | ``TIME(0)`` | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **array** [1]_ | ``array`` | **MySQL** | *all* | ``TINYTEXT`` [17]_ | +| **array** [1]_ | ``array`` | **MySQL** | *all* | ``TINYTEXT`` [16]_ | +-------------------+ | | +----------------------------------------------------------+ -| **simple array** | | | | ``TEXT`` [18]_ | +| **simple array** | | | | ``TEXT`` [17]_ | | [1]_ | | | +----------------------------------------------------------+ -| | | | | ``MEDIUMTEXT`` [19]_ | +| | | | | ``MEDIUMTEXT`` [18]_ | | | | | +----------------------------------------------------------+ -| | | | | ``LONGTEXT`` [20]_ | +| | | | | ``LONGTEXT`` [19]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``TEXT`` | | | +--------------------------+ | | | | | **SQL Anywhere** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``CLOB`` | | | +--------------------------+ | | @@ -773,25 +733,21 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``VARCHAR(MAX)`` | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **json_array** | ``array`` | **MySQL** [1]_ | *all* | ``TINYTEXT`` [17]_ | +| **json_array** | ``array`` | **MySQL** [1]_ | *all* | ``TINYTEXT`` [16]_ | | | | | +----------------------------------------------------------+ -| | | | | ``TEXT`` [18]_ | +| | | | | ``TEXT`` [17]_ | | | | | +----------------------------------------------------------+ -| | | | | ``MEDIUMTEXT`` [19]_ | +| | | | | ``MEDIUMTEXT`` [18]_ | | | | | +----------------------------------------------------------+ -| | | | | ``LONGTEXT`` [20]_ | +| | | | | ``LONGTEXT`` [19]_ | | | +--------------------------+---------+----------------------------------------------------------+ -| | | **PostgreSQL** | < 9.2 | ``TEXT`` [1]_ | -| | | +---------+----------------------------------------------------------+ -| | | | < 9.4 | ``JSON`` | +| | | **PostgreSQL** | < 9.4 | ``JSON`` | | | | +---------+----------------------------------------------------------+ -| | | | >= 9.4 | ``JSON`` [21]_ | +| | | | >= 9.4 | ``JSON`` [20]_ | | | | | +----------------------------------------------------------+ -| | | | | ``JSONB`` [22]_ | +| | | | | ``JSONB`` [21]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Anywhere** | *all* | ``TEXT`` [1]_ | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``CLOB`` [1]_ | | | +--------------------------+ | | @@ -799,19 +755,17 @@ Please also notice the mapping specific footnotes for additional information. | | +--------------------------+---------+----------------------------------------------------------+ | | | **SQL Server** | *all* | ``VARCHAR(MAX)`` [1]_ | +-------------------+---------------+--------------------------+---------+----------------------------------------------------------+ -| **object** [1]_ | ``object`` | **MySQL** | *all* | ``TINYTEXT`` [17]_ | +| **object** [1]_ | ``object`` | **MySQL** | *all* | ``TINYTEXT`` [16]_ | | | | | +----------------------------------------------------------+ -| | | | | ``TEXT`` [18]_ | +| | | | | ``TEXT`` [17]_ | | | | | +----------------------------------------------------------+ -| | | | | ``MEDIUMTEXT`` [19]_ | +| | | | | ``MEDIUMTEXT`` [18]_ | | | | | +----------------------------------------------------------+ -| | | | | ``LONGTEXT`` [20]_ | +| | | | | ``LONGTEXT`` [19]_ | | | +--------------------------+---------+----------------------------------------------------------+ | | | **PostgreSQL** | *all* | ``TEXT`` | | | +--------------------------+ | | | | | **SQL Anywhere** | | | -| | +--------------------------+ | | -| | | **Drizzle** | | | | | +--------------------------+---------+----------------------------------------------------------+ | | | **Oracle** | *all* | ``CLOB`` | | | +--------------------------+ | | @@ -827,10 +781,10 @@ Please also notice the mapping specific footnotes for additional information. .. [4] Chosen if the column definition has the **fixed** attribute set to ``true``. .. [5] Silently maps to the vendor specific ``text`` type if the given **length** attribute for **n** exceeds the maximum length the related platform allows. If this is the case, please - see [16]_. + see [15]_. .. [6] Silently maps to the vendor specific ``blob`` type if the given **length** attribute for **n** exceeds the maximum length the related platform allows. If this is the case, please - see [16]_. + see [15]_. .. [7] **p** is the precision and **s** the scale set in the column definition. The precision defaults to ``10`` and the scale to ``0`` if not set. .. [8] Returns PHP ``string`` type value instead of ``integer`` because of maximum integer value @@ -842,22 +796,20 @@ Please also notice the mapping specific footnotes for additional information. .. [12] Chosen if the column definition has the **autoincrement** attribute set to ``false`` (default). .. [13] Chosen if the column definition does not contain the **version** option inside the **platformOptions** attribute array or is set to ``false`` which marks it as a non-locking information column. -.. [14] Chosen if the column definition contains the **version** option inside the **platformOptions** - attribute array and is set to ``true`` which marks it as a locking information column. -.. [15] Fallback type as the vendor does not support a native date time type with timezone information. +.. [14] Fallback type as the vendor does not support a native date time type with timezone information. This means that the timezone information gets lost when storing a value. -.. [16] Cannot be safely reverse engineered to the same Doctrine type as the vendor does not have a +.. [15] Cannot be safely reverse engineered to the same Doctrine type as the vendor does not have a native distinct data type for this mapping. Using this type with this vendor can therefore have implications on schema comparison (*online* vs *offline* schema) and PHP type safety (data conversion from database to PHP value) because it silently falls back to its appropriate Doctrine type. -.. [17] Chosen if the column length is less or equal to **2 ^ 8 - 1 = 255**. -.. [18] Chosen if the column length is less or equal to **2 ^ 16 - 1 = 65535**. -.. [19] Chosen if the column length is less or equal to **2 ^ 24 - 1 = 16777215**. -.. [20] Chosen if the column length is less or equal to **2 ^ 32 - 1 = 4294967295** or empty. -.. [21] Chosen if the column definition does not contain the **jsonb** option inside the **platformOptions** +.. [16] Chosen if the column length is less or equal to **2 ^ 8 - 1 = 255**. +.. [17] Chosen if the column length is less or equal to **2 ^ 16 - 1 = 65535**. +.. [18] Chosen if the column length is less or equal to **2 ^ 24 - 1 = 16777215**. +.. [19] Chosen if the column length is less or equal to **2 ^ 32 - 1 = 4294967295** or empty. +.. [20] Chosen if the column definition does not contain the **jsonb** option inside the **platformOptions** attribute array or is set to ``false``. -.. [22] Chosen if the column definition contains the **jsonb** option inside the **platformOptions** +.. [21] Chosen if the column definition contains the **jsonb** option inside the **platformOptions** attribute array and is set to ``true``. Detection of Database Types diff --git a/lib/Doctrine/DBAL/Cache/ArrayStatement.php b/lib/Doctrine/DBAL/Cache/ArrayStatement.php index 449a220a2af..494876c935e 100644 --- a/lib/Doctrine/DBAL/Cache/ArrayStatement.php +++ b/lib/Doctrine/DBAL/Cache/ArrayStatement.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\FetchMode; use InvalidArgumentException; use IteratorAggregate; -use PDO; use function array_merge; use function array_values; use function count; @@ -59,9 +58,9 @@ public function columnCount() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { - if ($arg2 !== null || $arg3 !== null) { + if (count($args) > 0) { throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode()'); } @@ -83,7 +82,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { if (! isset($this->data[$this->num])) { return false; @@ -114,10 +113,10 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $rows = []; - while ($row = $this->fetch($fetchMode)) { + while ($row = $this->fetch($fetchMode, ...$args)) { $rows[] = $row; } diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 872f3e71fc9..b3f324f7136 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -9,7 +9,6 @@ use Doctrine\DBAL\FetchMode; use InvalidArgumentException; use IteratorAggregate; -use PDO; use function array_merge; use function array_values; use function reset; @@ -104,7 +103,7 @@ public function columnCount() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { $this->defaultFetchMode = $fetchMode; @@ -124,7 +123,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { if ($this->data === null) { $this->data = []; @@ -164,9 +163,9 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { - return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + return $this->statement->fetchAll($fetchMode, ...$args); } /** @@ -191,7 +190,7 @@ public function fetchColumn($columnIndex = 0) * * @return int The number of rows. */ - public function rowCount() + public function rowCount() : int { return $this->statement->rowCount(); } diff --git a/lib/Doctrine/DBAL/ColumnCase.php b/lib/Doctrine/DBAL/ColumnCase.php index 872d3ede873..c78ad372e65 100644 --- a/lib/Doctrine/DBAL/ColumnCase.php +++ b/lib/Doctrine/DBAL/ColumnCase.php @@ -2,8 +2,6 @@ namespace Doctrine\DBAL; -use PDO; - /** * Contains portable column case conversions. */ @@ -14,14 +12,14 @@ final class ColumnCase * * @see \PDO::CASE_UPPER */ - public const UPPER = PDO::CASE_UPPER; + public const UPPER = 1; /** * Convert column names to lower case. * * @see \PDO::CASE_LOWER */ - public const LOWER = PDO::CASE_LOWER; + public const LOWER = 2; /** * This class cannot be instantiated. diff --git a/lib/Doctrine/DBAL/Configuration.php b/lib/Doctrine/DBAL/Configuration.php index 9b7b21fb21e..a1ec0e97dfd 100644 --- a/lib/Doctrine/DBAL/Configuration.php +++ b/lib/Doctrine/DBAL/Configuration.php @@ -3,6 +3,7 @@ namespace Doctrine\DBAL; use Doctrine\Common\Cache\Cache; +use Doctrine\DBAL\Logging\NullLogger; use Doctrine\DBAL\Logging\SQLLogger; use Doctrine\DBAL\Schema\AbstractAsset; use function preg_match; @@ -24,23 +25,19 @@ class Configuration protected $_attributes = []; /** - * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. - * - * @return void + * Sets the SQL logger to use. */ - public function setSQLLogger(?SQLLogger $logger = null) + public function setSQLLogger(?SQLLogger $logger) : void { $this->_attributes['sqlLogger'] = $logger; } /** * Gets the SQL logger that is used. - * - * @return SQLLogger|null */ - public function getSQLLogger() + public function getSQLLogger() : SQLLogger { - return $this->_attributes['sqlLogger'] ?? null; + return $this->_attributes['sqlLogger'] ?? $this->_attributes['sqlLogger'] = new NullLogger(); } /** diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 85761fcca9b..1b7e4c49838 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -24,7 +24,6 @@ use function array_key_exists; use function array_merge; use function assert; -use function func_get_args; use function implode; use function is_int; use function is_string; @@ -815,18 +814,15 @@ public function quoteIdentifier($str) /** * Quotes a given input parameter. * - * @param mixed $input The parameter to be quoted. - * @param int|null $type The type of the parameter. + * @param string $input The parameter to be quoted. * * @return string The quoted parameter. */ - public function quote($input, $type = null) + public function quote(string $input) : string { $this->connect(); - [$value, $bindingType] = $this->getBindingInfo($input, $type); - - return $this->_conn->quote($value, $bindingType); + return $this->_conn->quote($input); } /** @@ -846,18 +842,16 @@ public function fetchAll($sql, array $params = [], $types = []) /** * Prepares an SQL statement. * - * @param string $statement The SQL statement to prepare. - * - * @return DriverStatement The prepared statement. + * @param string $sql The SQL statement to prepare. * * @throws DBALException */ - public function prepare($statement) + public function prepare(string $sql) : DriverStatement { try { - $stmt = new Statement($statement, $this); + $stmt = new Statement($sql, $this); } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $sql); } $stmt->setFetchMode($this->defaultFetchMode); @@ -873,14 +867,14 @@ public function prepare($statement) * * @param string $query The SQL query to execute. * @param mixed[] $params The parameters to bind to the query, if any. - * @param int[]|string[] $types The types the previous parameters are in. + * @param int[]|string[]|Type[] $types The types the previous parameters are in. * @param QueryCacheProfile|null $qcp The query cache profile, optional. * * @return ResultStatement The executed statement. * * @throws DBALException */ - public function executeQuery($query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) + public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) : ResultStatement { if ($qcp !== null) { return $this->executeCacheQuery($query, $params, $types, $qcp); @@ -889,9 +883,7 @@ public function executeQuery($query, array $params = [], $types = [], ?QueryCach $this->connect(); $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($query, $params, $types); - } + $logger->startQuery($query, $params, $types); try { if ($params) { @@ -913,9 +905,7 @@ public function executeQuery($query, array $params = [], $types = [], ?QueryCach $stmt->setFetchMode($this->defaultFetchMode); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); return $stmt; } @@ -923,16 +913,14 @@ public function executeQuery($query, array $params = [], $types = [], ?QueryCach /** * Executes a caching query. * - * @param string $query The SQL query to execute. - * @param mixed[] $params The parameters to bind to the query, if any. - * @param int[]|string[] $types The types the previous parameters are in. - * @param QueryCacheProfile $qcp The query cache profile. - * - * @return ResultStatement + * @param string $query The SQL query to execute. + * @param mixed[] $params The parameters to bind to the query, if any. + * @param int[]|string[]|Type[] $types The types the previous parameters are in. + * @param QueryCacheProfile $qcp The query cache profile. * * @throws CacheException */ - public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) + public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp) : ResultStatement { $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl(); if (! $resultCache) { @@ -989,34 +977,24 @@ public function project($query, array $params, Closure $function) } /** - * Executes an SQL statement, returning a result set as a Statement object. - * - * @return \Doctrine\DBAL\Driver\Statement - * - * @throws DBALException + * {@inheritDoc} */ - public function query() + public function query(string $sql) : ResultStatement { $this->connect(); - $args = func_get_args(); - $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($args[0]); - } + $logger->startQuery($sql); try { - $statement = $this->_conn->query(...$args); + $statement = $this->_conn->query($sql); } catch (Throwable $ex) { - throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $args[0]); + throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $sql); } $statement->setFetchMode($this->defaultFetchMode); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); return $statement; } @@ -1027,22 +1005,18 @@ public function query() * * This method supports PDO binding types as well as DBAL mapping types. * - * @param string $query The SQL query. - * @param mixed[] $params The query parameters. - * @param int[]|string[] $types The parameter types. - * - * @return int The number of affected rows. + * @param string $query The SQL query. + * @param mixed[] $params The query parameters. + * @param int[]|string[]|Type[] $types The parameter types. * * @throws DBALException */ - public function executeUpdate($query, array $params = [], array $types = []) + public function executeUpdate(string $query, array $params = [], array $types = []) : int { $this->connect(); $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($query, $params, $types); - } + $logger->startQuery($query, $params, $types); try { if ($params) { @@ -1063,30 +1037,20 @@ public function executeUpdate($query, array $params = [], array $types = []) throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $query, $this->resolveParams($params, $types)); } - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); return $result; } /** - * Executes an SQL statement and return the number of affected rows. - * - * @param string $statement - * - * @return int The number of affected rows. - * - * @throws DBALException + * {@inheritDoc} */ - public function exec($statement) + public function exec(string $statement) : int { $this->connect(); $logger = $this->_config->getSQLLogger(); - if ($logger) { - $logger->startQuery($statement); - } + $logger->startQuery($statement); try { $result = $this->_conn->exec($statement); @@ -1094,9 +1058,7 @@ public function exec($statement) throw DBALException::driverExceptionDuringQuery($this->_driver, $ex, $statement); } - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); return $result; } @@ -1145,7 +1107,7 @@ public function errorInfo() * * @return string A string representation of the last inserted ID. */ - public function lastInsertId($seqName = null) + public function lastInsertId(?string $seqName = null) : string { $this->connect(); @@ -1164,7 +1126,6 @@ public function lastInsertId($seqName = null) * * @return mixed The value returned by $func * - * @throws Exception * @throws Throwable */ public function transactional(Closure $func) @@ -1174,9 +1135,6 @@ public function transactional(Closure $func) $res = $func($this); $this->commit(); return $res; - } catch (Exception $e) { - $this->rollBack(); - throw $e; } catch (Throwable $e) { $this->rollBack(); throw $e; @@ -1228,10 +1186,8 @@ protected function _getNestedTransactionSavePointName() /** * Starts a transaction by suspending auto-commit mode. - * - * @return void */ - public function beginTransaction() + public function beginTransaction() : void { $this->connect(); @@ -1240,33 +1196,23 @@ public function beginTransaction() $logger = $this->_config->getSQLLogger(); if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"START TRANSACTION"'); - } + $logger->startQuery('"START TRANSACTION"'); $this->_conn->beginTransaction(); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"SAVEPOINT"'); - } + $logger->startQuery('"SAVEPOINT"'); $this->createSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); } } /** * Commits the current transaction. * - * @return void - * * @throws ConnectionException If the commit failed due to no active transaction or * because the transaction was marked for rollback only. */ - public function commit() + public function commit() : void { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); @@ -1280,21 +1226,13 @@ public function commit() $logger = $this->_config->getSQLLogger(); if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"COMMIT"'); - } + $logger->startQuery('"COMMIT"'); $this->_conn->commit(); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"RELEASE SAVEPOINT"'); - } + $logger->startQuery('"RELEASE SAVEPOINT"'); $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); } --$this->transactionNestingLevel; @@ -1329,7 +1267,7 @@ private function commitAll() * * @throws ConnectionException If the rollback operation failed. */ - public function rollBack() + public function rollBack() : void { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); @@ -1340,28 +1278,20 @@ public function rollBack() $logger = $this->_config->getSQLLogger(); if ($this->transactionNestingLevel === 1) { - if ($logger) { - $logger->startQuery('"ROLLBACK"'); - } + $logger->startQuery('"ROLLBACK"'); $this->transactionNestingLevel = 0; $this->_conn->rollBack(); $this->isRollbackOnly = false; - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); if ($this->autoCommit === false) { $this->beginTransaction(); } } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger) { - $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); - } + $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); --$this->transactionNestingLevel; - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); } else { $this->isRollbackOnly = true; --$this->transactionNestingLevel; @@ -1520,13 +1450,11 @@ public function convertToPHPValue($value, $type) * @internal Duck-typing used on the $stmt parameter to support driver statements as well as * raw PDOStatement instances. * - * @param \Doctrine\DBAL\Driver\Statement $stmt The statement to bind the values to. - * @param mixed[] $params The map/list of named/positional parameters. - * @param int[]|string[] $types The parameter types (PDO binding types or DBAL mapping types). - * - * @return void + * @param DriverStatement $stmt The statement to bind the values to. + * @param mixed[] $params The map/list of named/positional parameters. + * @param int[]|string[]|Type[] $types The parameter types. */ - private function _bindTypedValues($stmt, array $params, array $types) + private function _bindTypedValues(DriverStatement $stmt, array $params, array $types) : void { // Check whether parameters are positional or named. Mixing is not allowed, just like in PDO. if (is_int(key($params))) { diff --git a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php index e9864d6df90..e5da621a695 100644 --- a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php +++ b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php @@ -7,12 +7,13 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; use InvalidArgumentException; use function array_rand; use function count; -use function func_get_args; /** * Master-Slave Connection @@ -218,7 +219,7 @@ protected function chooseConnectionConfiguration($connectionName, $params) /** * {@inheritDoc} */ - public function executeUpdate($query, array $params = [], array $types = []) + public function executeUpdate(string $query, array $params = [], array $types = []) : int { $this->connect('master'); @@ -228,7 +229,7 @@ public function executeUpdate($query, array $params = [], array $types = []) /** * {@inheritDoc} */ - public function beginTransaction() + public function beginTransaction() : void { $this->connect('master'); @@ -238,7 +239,7 @@ public function beginTransaction() /** * {@inheritDoc} */ - public function commit() + public function commit() : void { $this->connect('master'); @@ -248,11 +249,11 @@ public function commit() /** * {@inheritDoc} */ - public function rollBack() + public function rollBack() : void { $this->connect('master'); - return parent::rollBack(); + parent::rollBack(); } /** @@ -301,7 +302,7 @@ public function insert($tableName, array $data, array $types = []) /** * {@inheritDoc} */ - public function exec($statement) + public function exec(string $statement) : int { $this->connect('master'); @@ -341,22 +342,16 @@ public function rollbackSavepoint($savepoint) /** * {@inheritDoc} */ - public function query() + public function query(string $sql) : ResultStatement { $this->connect('master'); - $args = func_get_args(); - $logger = $this->getConfiguration()->getSQLLogger(); - if ($logger) { - $logger->startQuery($args[0]); - } + $logger->startQuery($sql); - $statement = $this->_conn->query(...$args); + $statement = $this->_conn->query($sql); - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); return $statement; } @@ -364,10 +359,10 @@ public function query() /** * {@inheritDoc} */ - public function prepare($statement) + public function prepare(string $sql) : Statement { $this->connect('master'); - return parent::prepare($statement); + return parent::prepare($sql); } } diff --git a/lib/Doctrine/DBAL/Driver/AbstractDriverException.php b/lib/Doctrine/DBAL/Driver/AbstractDriverException.php index d9af92d1744..41f6d63f3e2 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractDriverException.php +++ b/lib/Doctrine/DBAL/Driver/AbstractDriverException.php @@ -36,6 +36,42 @@ public function __construct($message, $sqlState = null, $errorCode = null) $this->sqlState = $sqlState; } + /** + * @return AbstractDriverException + */ + public static function noInsertId() : self + { + $message = 'No identity value was generated by the last statement.'; + + if (static::class === self::class) { + // WIP regarding exceptions, see: + // https://github.com/doctrine/dbal/pull/3335#discussion_r234381175 + return new class($message) extends AbstractDriverException { + }; + } + + return new static($message); + } + + /** + * @param string $name The sequence name. + * + * @return AbstractDriverException + */ + public static function noSuchSequence(string $name) : self + { + $message = 'No sequence with name "' . $name . '" found.'; + + if (static::class === self::class) { + // WIP regarding exceptions, see: + // https://github.com/doctrine/dbal/pull/3335#discussion_r234381175 + return new class($message) extends AbstractDriverException { + }; + } + + return new static($message); + } + /** * {@inheritdoc} */ diff --git a/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php b/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php index 916d924980c..bdfd6cd894b 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractPostgreSQLDriver.php @@ -7,8 +7,6 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\PostgreSQL100Platform; -use Doctrine\DBAL\Platforms\PostgreSQL91Platform; -use Doctrine\DBAL\Platforms\PostgreSQL92Platform; use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Schema\PostgreSqlSchemaManager; @@ -101,10 +99,6 @@ public function createDatabasePlatformForVersion($version) return new PostgreSQL100Platform(); case version_compare($version, '9.4', '>='): return new PostgreSQL94Platform(); - case version_compare($version, '9.2', '>='): - return new PostgreSQL92Platform(); - case version_compare($version, '9.1', '>='): - return new PostgreSQL91Platform(); default: return new PostgreSqlPlatform(); } diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php b/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php index 88f26ce7def..f93b576e202 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLAnywhereDriver.php @@ -6,14 +6,10 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Platforms\SQLAnywhere11Platform; -use Doctrine\DBAL\Platforms\SQLAnywhere12Platform; -use Doctrine\DBAL\Platforms\SQLAnywhere16Platform; use Doctrine\DBAL\Platforms\SQLAnywherePlatform; use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager; use Doctrine\DBAL\VersionAwarePlatformDriver; use function preg_match; -use function version_compare; /** * Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers. @@ -81,19 +77,7 @@ public function createDatabasePlatformForVersion($version) ); } - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? 0; - $buildVersion = $versionParts['build'] ?? 0; - $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion; - switch (true) { - case version_compare($version, '16', '>='): - return new SQLAnywhere16Platform(); - case version_compare($version, '12', '>='): - return new SQLAnywhere12Platform(); - case version_compare($version, '11', '>='): - return new SQLAnywhere11Platform(); default: return new SQLAnywherePlatform(); } @@ -114,7 +98,7 @@ public function getDatabase(Connection $conn) */ public function getDatabasePlatform() { - return new SQLAnywhere12Platform(); + return new SQLAnywherePlatform(); } /** diff --git a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php index 421d82b3951..280f72544e0 100644 --- a/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php +++ b/lib/Doctrine/DBAL/Driver/AbstractSQLServerDriver.php @@ -5,8 +5,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Platforms\SQLServer2005Platform; -use Doctrine\DBAL\Platforms\SQLServer2008Platform; use Doctrine\DBAL\Platforms\SQLServer2012Platform; use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\DBAL\Schema\SQLServerSchemaManager; @@ -44,10 +42,6 @@ public function createDatabasePlatformForVersion($version) switch (true) { case version_compare($version, '11.00.2100', '>='): return new SQLServer2012Platform(); - case version_compare($version, '10.00.1600', '>='): - return new SQLServer2008Platform(); - case version_compare($version, '9.00.1399', '>='): - return new SQLServer2005Platform(); default: return new SQLServerPlatform(); } @@ -68,7 +62,7 @@ public function getDatabase(Connection $conn) */ public function getDatabasePlatform() { - return new SQLServer2008Platform(); + return new SQLServerPlatform(); } /** diff --git a/lib/Doctrine/DBAL/Driver/Connection.php b/lib/Doctrine/DBAL/Driver/Connection.php index 1574581c2ad..51278b51a23 100644 --- a/lib/Doctrine/DBAL/Driver/Connection.php +++ b/lib/Doctrine/DBAL/Driver/Connection.php @@ -2,7 +2,7 @@ namespace Doctrine\DBAL\Driver; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\DBALException; /** * Connection interface. @@ -15,67 +15,85 @@ interface Connection /** * Prepares a statement for execution and returns a Statement object. * - * @param string $prepareString + * @param string $sql The SQL query to prepare. * - * @return Statement + * @return Statement The prepared statement. */ - public function prepare($prepareString); + public function prepare(string $sql) : Statement; /** * Executes an SQL statement, returning a result set as a Statement object. * - * @return Statement + * @param string $sql The SQL query to execute. + * + * @return ResultStatement The result statement. + * + * @throws DBALException */ - public function query(); + public function query(string $sql) : ResultStatement; /** * Quotes a string for use in a query. * - * @param mixed $input - * @param int $type + * @param string $input The parameter to quote. * - * @return mixed + * @return string The quoted string. */ - public function quote($input, $type = ParameterType::STRING); + public function quote(string $input) : string; /** * Executes an SQL statement and return the number of affected rows. * - * @param string $statement + * @param string $statement The SQL query to execute. + * + * @return int The number of affected rows. * - * @return int + * @throws DBALException */ - public function exec($statement); + public function exec(string $statement) : int; /** * Returns the ID of the last inserted row or sequence value. * - * @param string|null $name + * If a sequence name was not specified, lastInsertId() returns a string representing the value of the + * auto-increment field from the last row inserted into the database, if any. + * + * If a sequence name was specified, lastInsertId() returns a string representing the current value of the sequence. + * + * This method throws a DriverException if a value cannot be returned, in particular when: + * + * - this operation is not supported by the driver; + * - no sequence name was provided, but the driver requires one; + * - no sequence name was provided, but the last statement dit not return an identity (caution: see note below); + * - a sequence name was provided, but the driver does not support sequences; + * - a sequence name was provided, but the sequence does not exist. * - * @return string + * Note: if the last statement was not an INSERT to an autoincrement column, this method MAY return an ID from a + * previous statement. DO NOT RELY ON THIS BEHAVIOR which is driver-dependent: always use lastInsertId() right after + * executing an INSERT statement. + * + * @param string|null $name The sequence name, or NULL to return the ID of the last row inserted. + * + * @return string The last insert ID or sequence value. + * + * @throws DriverException If an error occurs. */ - public function lastInsertId($name = null); + public function lastInsertId(?string $name = null) : string; /** * Initiates a transaction. - * - * @return bool TRUE on success or FALSE on failure. */ - public function beginTransaction(); + public function beginTransaction() : void; /** * Commits a transaction. - * - * @return bool TRUE on success or FALSE on failure. */ - public function commit(); + public function commit() : void; /** * Rolls back the current transaction, as initiated by beginTransaction(). - * - * @return bool TRUE on success or FALSE on failure. */ - public function rollBack(); + public function rollBack() : void; /** * Returns the error code associated with the last operation on the database handle. diff --git a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php b/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php deleted file mode 100644 index 4089ab26e11..00000000000 --- a/lib/Doctrine/DBAL/Driver/DrizzlePDOMySql/Connection.php +++ /dev/null @@ -1,21 +0,0 @@ -constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } - - /** - * {@inheritdoc} - */ - public function createDatabasePlatformForVersion($version) - { - return $this->getDatabasePlatform(); - } - - /** - * {@inheritdoc} - */ - public function getDatabasePlatform() - { - return new DrizzlePlatform(); - } - - /** - * {@inheritdoc} - */ - public function getSchemaManager(\Doctrine\DBAL\Connection $conn) - { - return new DrizzleSchemaManager($conn); - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'drizzle_pdo_mysql'; - } -} diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php index bd80e6e49a7..cde9a27ef8d 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Connection.php @@ -3,8 +3,9 @@ namespace Doctrine\DBAL\Driver\IBMDB2; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use stdClass; use const DB2_AUTOCOMMIT_OFF; use const DB2_AUTOCOMMIT_ON; @@ -22,7 +23,6 @@ use function db2_rollback; use function db2_server_info; use function db2_stmt_errormsg; -use function func_get_args; class DB2Connection implements Connection, ServerInfoAwareConnection { @@ -73,7 +73,7 @@ public function requiresQueryForServerVersion() /** * {@inheritdoc} */ - public function prepare($sql) + public function prepare(string $sql) : DriverStatement { $stmt = @db2_prepare($this->conn, $sql); if (! $stmt) { @@ -86,10 +86,8 @@ public function prepare($sql) /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $sql = $args[0]; $stmt = $this->prepare($sql); $stmt->execute(); @@ -99,21 +97,17 @@ public function query() /** * {@inheritdoc} */ - public function quote($input, $type = ParameterType::STRING) + public function quote(string $input) : string { $input = db2_escape_string($input); - if ($type === ParameterType::INTEGER) { - return $input; - } - return "'" . $input . "'"; } /** * {@inheritdoc} */ - public function exec($statement) + public function exec(string $statement) : int { $stmt = @db2_exec($this->conn, $statement); @@ -127,15 +121,25 @@ public function exec($statement) /** * {@inheritdoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { - return db2_last_insert_id($this->conn); + if ($name !== null) { + throw new DB2Exception('Sequences on IBM DB2 are not currently supported.'); + } + + $lastInsertId = db2_last_insert_id($this->conn); + + if ($lastInsertId === '') { + throw DB2Exception::noInsertId(); + } + + return $lastInsertId; } /** * {@inheritdoc} */ - public function beginTransaction() + public function beginTransaction() : void { db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF); } @@ -143,7 +147,7 @@ public function beginTransaction() /** * {@inheritdoc} */ - public function commit() + public function commit() : void { if (! db2_commit($this->conn)) { throw new DB2Exception(db2_conn_errormsg($this->conn)); @@ -154,7 +158,7 @@ public function commit() /** * {@inheritdoc} */ - public function rollBack() + public function rollBack() : void { if (! db2_rollback($this->conn)) { throw new DB2Exception(db2_conn_errormsg($this->conn)); diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php index b01c4552a23..cb8e626fcdb 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Exception.php @@ -2,8 +2,8 @@ namespace Doctrine\DBAL\Driver\IBMDB2; -use Exception; +use Doctrine\DBAL\Driver\AbstractDriverException; -class DB2Exception extends Exception +class DB2Exception extends AbstractDriverException { } diff --git a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php index 9c87d46319b..5e6e112858a 100644 --- a/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php +++ b/lib/Doctrine/DBAL/Driver/IBMDB2/DB2Statement.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use IteratorAggregate; -use PDO; use ReflectionClass; use ReflectionObject; use ReflectionProperty; @@ -19,6 +18,7 @@ use const DB2_PARAM_FILE; use const DB2_PARAM_IN; use function array_change_key_case; +use function count; use function db2_bind_param; use function db2_execute; use function db2_fetch_array; @@ -32,8 +32,6 @@ use function db2_stmt_errormsg; use function error_get_last; use function fclose; -use function func_get_args; -use function func_num_args; use function fwrite; use function gettype; use function is_object; @@ -241,11 +239,17 @@ public function execute($params = null) /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + $this->defaultFetchMode = $fetchMode; + + if (isset($args[0])) { + $this->defaultFetchClass = $args[0]; + } + + if (isset($args[1])) { + $this->defaultFetchClassCtorArgs = (array) $args[2]; + } return true; } @@ -261,7 +265,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { // do not try fetching from the statement if it's not expected to contain result // in order to prevent exceptional situation @@ -284,10 +288,9 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX $className = $this->defaultFetchClass; $ctorArgs = $this->defaultFetchClassCtorArgs; - if (func_num_args() >= 2) { - $args = func_get_args(); - $className = $args[1]; - $ctorArgs = $args[2] ?? []; + if (count($args) > 0) { + $className = $args[0]; + $ctorArgs = $args[1] ?? []; } $result = db2_fetch_object($this->stmt); @@ -312,13 +315,13 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $rows = []; switch ($fetchMode) { case FetchMode::CUSTOM_OBJECT: - while (($row = $this->fetch(...func_get_args())) !== false) { + while (($row = $this->fetch($fetchMode, ...$args)) !== false) { $rows[] = $row; } break; @@ -353,7 +356,7 @@ public function fetchColumn($columnIndex = 0) /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { return @db2_num_rows($this->stmt) ? : 0; } diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php index 1f1a1d218c3..4dd413db4c0 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliConnection.php @@ -4,8 +4,9 @@ use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Driver\PingableConnection; +use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use mysqli; use const MYSQLI_INIT_COMMAND; use const MYSQLI_OPT_CONNECT_TIMEOUT; @@ -15,7 +16,6 @@ use const MYSQLI_SERVER_PUBLIC_KEY; use function defined; use function floor; -use function func_get_args; use function in_array; use function ini_get; use function mysqli_errno; @@ -126,18 +126,16 @@ public function requiresQueryForServerVersion() /** * {@inheritdoc} */ - public function prepare($prepareString) + public function prepare(string $sql) : DriverStatement { - return new MysqliStatement($this->conn, $prepareString); + return new MysqliStatement($this->conn, $sql); } /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $sql = $args[0]; $stmt = $this->prepare($sql); $stmt->execute(); @@ -147,7 +145,7 @@ public function query() /** * {@inheritdoc} */ - public function quote($input, $type = ParameterType::STRING) + public function quote(string $input) : string { return "'" . $this->conn->escape_string($input) . "'"; } @@ -155,7 +153,7 @@ public function quote($input, $type = ParameterType::STRING) /** * {@inheritdoc} */ - public function exec($statement) + public function exec(string $statement) : int { if ($this->conn->query($statement) === false) { throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno); @@ -167,35 +165,39 @@ public function exec($statement) /** * {@inheritdoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { - return $this->conn->insert_id; + $insertId = $this->conn->insert_id; + + if ($insertId === 0) { + throw MysqliException::noInsertId(); + } + + return (string) $insertId; } /** * {@inheritdoc} */ - public function beginTransaction() + public function beginTransaction() : void { $this->conn->query('START TRANSACTION'); - - return true; } /** * {@inheritdoc} */ - public function commit() + public function commit() : void { - return $this->conn->commit(); + $this->conn->commit(); } /** * {@inheritdoc}non-PHPdoc) */ - public function rollBack() + public function rollBack() : void { - return $this->conn->rollback(); + $this->conn->rollback(); } /** diff --git a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php index 2bbe0ca8b20..0414524447e 100644 --- a/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php +++ b/lib/Doctrine/DBAL/Driver/Mysqli/MysqliStatement.php @@ -10,7 +10,6 @@ use IteratorAggregate; use mysqli; use mysqli_stmt; -use PDO; use stdClass; use function array_combine; use function array_fill; @@ -304,7 +303,7 @@ private function _fetch() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { // do not try fetching from the statement if it's not expected to contain result // in order to prevent exceptional situation @@ -358,7 +357,7 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $fetchMode = $fetchMode ?: $this->_defaultFetchMode; @@ -421,7 +420,7 @@ public function closeCursor() /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { if ($this->_columnNames === false) { return $this->_stmt->affected_rows; @@ -441,7 +440,7 @@ public function columnCount() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { $this->_defaultFetchMode = $fetchMode; diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php index 5ebc877a898..76266cb44b3 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Connection.php @@ -3,8 +3,9 @@ namespace Doctrine\DBAL\Driver\OCI8; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use UnexpectedValueException; use const OCI_COMMIT_ON_SUCCESS; use const OCI_DEFAULT; @@ -12,9 +13,6 @@ use function addcslashes; use function define; use function defined; -use function func_get_args; -use function is_float; -use function is_int; use function oci_commit; use function oci_connect; use function oci_error; @@ -95,19 +93,16 @@ public function requiresQueryForServerVersion() /** * {@inheritdoc} */ - public function prepare($prepareString) + public function prepare(string $sql) : DriverStatement { - return new OCI8Statement($this->dbh, $prepareString, $this); + return new OCI8Statement($this->dbh, $sql, $this); } /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $sql = $args[0]; - //$fetchMode = $args[1]; $stmt = $this->prepare($sql); $stmt->execute(); @@ -117,11 +112,8 @@ public function query() /** * {@inheritdoc} */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value) : string { - if (is_int($value) || is_float($value)) { - return $value; - } $value = str_replace("'", "''", $value); return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; @@ -130,7 +122,7 @@ public function quote($value, $type = ParameterType::STRING) /** * {@inheritdoc} */ - public function exec($statement) + public function exec(string $statement) : int { $stmt = $this->prepare($statement); $stmt->execute(); @@ -141,10 +133,10 @@ public function exec($statement) /** * {@inheritdoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { if ($name === null) { - return false; + throw new OCI8Exception('A sequence name must be provided.'); } $sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL'; @@ -155,7 +147,7 @@ public function lastInsertId($name = null) throw new OCI8Exception('lastInsertId failed: Query was executed but no result was returned.'); } - return (int) $result; + return $result; } /** @@ -171,37 +163,31 @@ public function getExecuteMode() /** * {@inheritdoc} */ - public function beginTransaction() + public function beginTransaction() : void { $this->executeMode = OCI_NO_AUTO_COMMIT; - - return true; } /** * {@inheritdoc} */ - public function commit() + public function commit() : void { if (! oci_commit($this->dbh)) { throw OCI8Exception::fromErrorInfo($this->errorInfo()); } $this->executeMode = OCI_COMMIT_ON_SUCCESS; - - return true; } /** * {@inheritdoc} */ - public function rollBack() + public function rollBack() : void { if (! oci_rollback($this->dbh)) { throw OCI8Exception::fromErrorInfo($this->errorInfo()); } $this->executeMode = OCI_COMMIT_ON_SUCCESS; - - return true; } /** diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php index 8db54c86aa7..1a8de4a9c4b 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Statement.php @@ -8,7 +8,6 @@ use Doctrine\DBAL\ParameterType; use InvalidArgumentException; use IteratorAggregate; -use PDO; use const OCI_ASSOC; use const OCI_B_BIN; use const OCI_B_BLOB; @@ -380,7 +379,7 @@ public function execute($params = null) /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { $this->_defaultFetchMode = $fetchMode; @@ -398,7 +397,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { // do not try fetching from the statement if it's not expected to contain result // in order to prevent exceptional situation @@ -429,7 +428,7 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $fetchMode = $fetchMode ?: $this->_defaultFetchMode; @@ -503,7 +502,7 @@ public function fetchColumn($columnIndex = 0) /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { return oci_num_rows($this->_sth); } diff --git a/lib/Doctrine/DBAL/Driver/PDOConnection.php b/lib/Doctrine/DBAL/Driver/PDOConnection.php index 3368c0d94dc..5f60b02487e 100644 --- a/lib/Doctrine/DBAL/Driver/PDOConnection.php +++ b/lib/Doctrine/DBAL/Driver/PDOConnection.php @@ -2,17 +2,18 @@ namespace Doctrine\DBAL\Driver; -use Doctrine\DBAL\ParameterType; use PDO; -use function count; -use function func_get_args; /** * PDO implementation of the Connection interface. + * * Used by all PDO-based drivers. */ -class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection +class PDOConnection implements Connection, ServerInfoAwareConnection { + /** @var PDO */ + private $connection; + /** * @param string $dsn * @param string|null $user @@ -24,9 +25,8 @@ class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection public function __construct($dsn, $user = null, $password = null, ?array $options = null) { try { - parent::__construct($dsn, $user, $password, $options); - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]); - $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->connection = new PDO($dsn, $user, $password, $options); + $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -35,10 +35,10 @@ public function __construct($dsn, $user = null, $password = null, ?array $option /** * {@inheritdoc} */ - public function exec($statement) + public function exec(string $statement) : int { try { - return parent::exec($statement); + return $this->connection->exec($statement); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -49,16 +49,18 @@ public function exec($statement) */ public function getServerVersion() { - return PDO::getAttribute(PDO::ATTR_SERVER_VERSION); + return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); } /** * {@inheritdoc} */ - public function prepare($prepareString, $driverOptions = []) + public function prepare(string $sql) : Statement { try { - return parent::prepare($prepareString, $driverOptions); + return $this->createStatement( + $this->connection->prepare($sql) + ); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -67,25 +69,12 @@ public function prepare($prepareString, $driverOptions = []) /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $argsCount = count($args); - try { - if ($argsCount === 4) { - return parent::query($args[0], $args[1], $args[2], $args[3]); - } - - if ($argsCount === 3) { - return parent::query($args[0], $args[1], $args[2]); - } - - if ($argsCount === 2) { - return parent::query($args[0], $args[1]); - } - - return parent::query($args[0]); + return $this->createStatement( + $this->connection->query($sql) + ); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -94,17 +83,28 @@ public function query() /** * {@inheritdoc} */ - public function quote($input, $type = ParameterType::STRING) + public function quote(string $input) : string { - return parent::quote($input, $type); + return $this->connection->quote($input); } /** * {@inheritdoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { - return parent::lastInsertId($name); + try { + $lastInsertId = $this->connection->lastInsertId($name); + } catch (\PDOException $e) { + throw new PDOException($e); + } + + // pdo_mysql and others return '0', pdo_sqlsrv returns '' + if ($lastInsertId === '0' || $lastInsertId === '') { + throw AbstractDriverException::noInsertId(); + } + + return $lastInsertId; } /** @@ -114,4 +114,57 @@ public function requiresQueryForServerVersion() { return false; } + + /** + * Creates a wrapped statement + */ + protected function createStatement(\PDOStatement $stmt) : PDOStatement + { + return new PDOStatement($stmt); + } + + /** + * {@inheritDoc} + */ + public function beginTransaction() : void + { + $this->connection->beginTransaction(); + } + + /** + * {@inheritDoc} + */ + public function commit() : void + { + $this->connection->commit(); + } + + /** + * {@inheritDoc} + */ + public function rollBack() : void + { + $this->connection->rollBack(); + } + + /** + * {@inheritDoc} + */ + public function errorCode() + { + return $this->connection->errorCode(); + } + + /** + * {@inheritDoc} + */ + public function errorInfo() + { + return $this->connection->errorInfo(); + } + + public function getWrappedConnection() : PDO + { + return $this->connection; + } } diff --git a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php b/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php deleted file mode 100644 index 4291e1e17f0..00000000000 --- a/lib/Doctrine/DBAL/Driver/PDOIbm/Driver.php +++ /dev/null @@ -1,57 +0,0 @@ -_constructPdoDsn($params), - $username, - $password, - $driverOptions - ); - } - - /** - * Constructs the IBM PDO DSN. - * - * @param mixed[] $params - * - * @return string The DSN. - */ - private function _constructPdoDsn(array $params) - { - $dsn = 'ibm:'; - if (isset($params['host'])) { - $dsn .= 'HOSTNAME=' . $params['host'] . ';'; - } - if (isset($params['port'])) { - $dsn .= 'PORT=' . $params['port'] . ';'; - } - $dsn .= 'PROTOCOL=TCPIP;'; - if (isset($params['dbname'])) { - $dsn .= 'DATABASE=' . $params['dbname'] . ';'; - } - - return $dsn; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'pdo_ibm'; - } -} diff --git a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php index 972dbadcd5d..c3b9e39a026 100644 --- a/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php +++ b/lib/Doctrine/DBAL/Driver/PDOPgSql/Driver.php @@ -20,7 +20,7 @@ class Driver extends AbstractPostgreSQLDriver public function connect(array $params, $username = null, $password = null, array $driverOptions = []) { try { - $pdo = new PDOConnection( + $connection = new PDOConnection( $this->_constructPdoDsn($params), $username, $password, @@ -32,7 +32,7 @@ public function connect(array $params, $username = null, $password = null, array || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true ) ) { - $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); + $connection->getWrappedConnection()->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); } /* defining client_encoding via SET NAMES to avoid inconsistent DSN support @@ -40,10 +40,10 @@ public function connect(array $params, $username = null, $password = null, array * - passing client_encoding via the 'options' param breaks pgbouncer support */ if (isset($params['charset'])) { - $pdo->exec('SET NAMES \'' . $params['charset'] . '\''); + $connection->exec('SET NAMES \'' . $params['charset'] . '\''); } - return $pdo; + return $connection; } catch (PDOException $e) { throw DBALException::driverException($this, $e); } diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlite/Connection.php b/lib/Doctrine/DBAL/Driver/PDOSqlite/Connection.php new file mode 100644 index 00000000000..8a12fa0f243 --- /dev/null +++ b/lib/Doctrine/DBAL/Driver/PDOSqlite/Connection.php @@ -0,0 +1,30 @@ +_constructPdoDsn($params), $username, $password, @@ -45,11 +44,13 @@ public function connect(array $params, $username = null, $password = null, array throw DBALException::driverException($this, $ex); } + $pdo = $connection->getWrappedConnection(); + foreach ($this->_userDefinedFunctions as $fn => $data) { $pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']); } - return $pdo; + return $connection; } /** diff --git a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php index 86ad9aedf29..acf6b797291 100644 --- a/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php +++ b/lib/Doctrine/DBAL/Driver/PDOSqlsrv/Connection.php @@ -2,9 +2,9 @@ namespace Doctrine\DBAL\Driver\PDOSqlsrv; +use Doctrine\DBAL\Driver\AbstractDriverException; use Doctrine\DBAL\Driver\PDOConnection; -use Doctrine\DBAL\ParameterType; -use PDO; +use Doctrine\DBAL\Driver\PDOStatement; use function strpos; use function substr; @@ -13,19 +13,10 @@ */ class Connection extends PDOConnection implements \Doctrine\DBAL\Driver\Connection { - /** - * {@inheritdoc} - */ - public function __construct($dsn, $user = null, $password = null, ?array $options = null) - { - parent::__construct($dsn, $user, $password, $options); - $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]); - } - /** * {@inheritDoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { if ($name === null) { return parent::lastInsertId($name); @@ -34,15 +25,21 @@ public function lastInsertId($name = null) $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); $stmt->execute([$name]); - return $stmt->fetchColumn(); + $result = $stmt->fetchColumn(); + + if ($result === false) { + throw AbstractDriverException::noSuchSequence($name); + } + + return (string) $result; } /** * {@inheritDoc} */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value) : string { - $val = parent::quote($value, $type); + $val = parent::quote($value); // Fix for a driver version terminating all values with null byte if (strpos($val, "\0") !== false) { @@ -51,4 +48,12 @@ public function quote($value, $type = ParameterType::STRING) return $val; } + + /** + * {@inheritDoc} + */ + protected function createStatement(\PDOStatement $stmt) : PDOStatement + { + return new Statement($stmt); + } } diff --git a/lib/Doctrine/DBAL/Driver/PDOStatement.php b/lib/Doctrine/DBAL/Driver/PDOStatement.php index 2aed89fbf07..0d8f9bf82fc 100644 --- a/lib/Doctrine/DBAL/Driver/PDOStatement.php +++ b/lib/Doctrine/DBAL/Driver/PDOStatement.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; +use IteratorAggregate; use PDO; use const E_USER_DEPRECATED; use function sprintf; @@ -13,7 +14,7 @@ * The PDO implementation of the Statement interface. * Used by all PDO-based drivers. */ -class PDOStatement extends \PDOStatement implements Statement +class PDOStatement implements IteratorAggregate, Statement { private const PARAM_TYPE_MAP = [ ParameterType::NULL => PDO::PARAM_NULL, @@ -33,34 +34,23 @@ class PDOStatement extends \PDOStatement implements Statement FetchMode::CUSTOM_OBJECT => PDO::FETCH_CLASS, ]; - /** - * Protected constructor. - */ - protected function __construct() + /** @var \PDOStatement */ + private $stmt; + + public function __construct(\PDOStatement $stmt) { + $this->stmt = $stmt; } /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { $fetchMode = $this->convertFetchMode($fetchMode); - // This thin wrapper is necessary to shield against the weird signature - // of PDOStatement::setFetchMode(): even if the second and third - // parameters are optional, PHP will not let us remove it from this - // declaration. try { - if ($arg2 === null && $arg3 === null) { - return parent::setFetchMode($fetchMode); - } - - if ($arg3 === null) { - return parent::setFetchMode($fetchMode, $arg2); - } - - return parent::setFetchMode($fetchMode, $arg2, $arg3); + return $this->stmt->setFetchMode($fetchMode, ...$args); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -74,7 +64,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING) $type = $this->convertParamType($type); try { - return parent::bindValue($param, $value, $type); + return $this->stmt->bindValue($param, $value, $type); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -88,7 +78,7 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l $type = $this->convertParamType($type); try { - return parent::bindParam($column, $variable, $type, $length, $driverOptions); + return $this->stmt->bindParam($column, $variable, $type, $length, $driverOptions); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -100,7 +90,7 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l public function closeCursor() { try { - return parent::closeCursor(); + return $this->stmt->closeCursor(); } catch (\PDOException $exception) { // Exceptions not allowed by the interface. // In case driver implementations do not adhere to the interface, silence exceptions here. @@ -108,13 +98,37 @@ public function closeCursor() } } + /** + * {@inheritdoc} + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * {@inheritdoc} + */ + public function errorCode() + { + return $this->stmt->errorCode(); + } + + /** + * {@inheritdoc} + */ + public function errorInfo() + { + return $this->stmt->errorInfo(); + } + /** * {@inheritdoc} */ public function execute($params = null) { try { - return parent::execute($params); + return $this->stmt->execute($params); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -123,24 +137,24 @@ public function execute($params = null) /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function rowCount() : int + { + return $this->stmt->rowCount(); + } + + /** + * {@inheritdoc} + */ + public function fetch($fetchMode = null, ...$args) { $fetchMode = $this->convertFetchMode($fetchMode); try { - if ($fetchMode === null && $cursorOrientation === PDO::FETCH_ORI_NEXT && $cursorOffset === 0) { - return parent::fetch(); - } - - if ($cursorOrientation === PDO::FETCH_ORI_NEXT && $cursorOffset === 0) { - return parent::fetch($fetchMode); - } - - if ($cursorOffset === 0) { - return parent::fetch($fetchMode, $cursorOrientation); + if ($fetchMode === null) { + return $this->stmt->fetch(); } - return parent::fetch($fetchMode, $cursorOrientation, $cursorOffset); + return $this->stmt->fetch($fetchMode, ...$args); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -149,24 +163,16 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $fetchMode = $this->convertFetchMode($fetchMode); try { - if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) { - return parent::fetchAll(); - } - - if ($fetchArgument === null && $ctorArgs === null) { - return parent::fetchAll($fetchMode); + if ($fetchMode === null) { + return $this->stmt->fetchAll(); } - if ($ctorArgs === null) { - return parent::fetchAll($fetchMode, $fetchArgument); - } - - return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs); + return $this->stmt->fetchAll($fetchMode, ...$args); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -178,7 +184,7 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n public function fetchColumn($columnIndex = 0) { try { - return parent::fetchColumn($columnIndex); + return $this->stmt->fetchColumn($columnIndex); } catch (\PDOException $exception) { throw new PDOException($exception); } @@ -228,4 +234,12 @@ private function convertFetchMode(?int $fetchMode) : ?int return self::FETCH_MODE_MAP[$fetchMode]; } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + yield from $this->stmt; + } } diff --git a/lib/Doctrine/DBAL/Driver/ResultStatement.php b/lib/Doctrine/DBAL/Driver/ResultStatement.php index 1e6df0269b8..70856e1a153 100644 --- a/lib/Doctrine/DBAL/Driver/ResultStatement.php +++ b/lib/Doctrine/DBAL/Driver/ResultStatement.php @@ -2,7 +2,6 @@ namespace Doctrine\DBAL\Driver; -use PDO; use Traversable; /** @@ -29,62 +28,43 @@ public function columnCount(); /** * Sets the fetch mode to use while iterating this statement. * - * @param int $fetchMode The fetch mode must be one of the {@link \Doctrine\DBAL\FetchMode} constants. - * @param mixed $arg2 - * @param mixed $arg3 + * @param int $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants. + * @param mixed[] ...$args Optional mode-specific arguments (see {@link self::fetchAll()}). * * @return bool */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null); + public function setFetchMode($fetchMode, ...$args); /** * Returns the next row of a result set. * - * @param int|null $fetchMode Controls how the next row will be returned to the caller. - * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants, - * defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}. - * @param int $cursorOrientation For a ResultStatement object representing a scrollable cursor, - * this value determines which row will be returned to the caller. - * This value must be one of the \PDO::FETCH_ORI_* constants, - * defaulting to \PDO::FETCH_ORI_NEXT. To request a scrollable - * cursor for your ResultStatement object, you must set the \PDO::ATTR_CURSOR - * attribute to \PDO::CURSOR_SCROLL when you prepare the SQL statement with - * \PDO::prepare(). - * @param int $cursorOffset For a ResultStatement object representing a scrollable cursor for which the - * cursorOrientation parameter is set to \PDO::FETCH_ORI_ABS, this value - * specifies the absolute number of the row in the result set that shall be - * fetched. - * For a ResultStatement object representing a scrollable cursor for which the - * cursorOrientation parameter is set to \PDO::FETCH_ORI_REL, this value - * specifies the row to fetch relative to the cursor position before - * ResultStatement::fetch() was called. + * @param int|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants, + * defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}. + * @param mixed[] ...$args Optional mode-specific arguments (see {@link self::fetchAll()}). * * @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is * returned on failure. */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0); + public function fetch($fetchMode = null, ...$args); /** * Returns an array containing all of the result set rows. * - * @param int|null $fetchMode Controls how the next row will be returned to the caller. - * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants, - * defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}. - * @param int|null $fetchArgument This argument has a different meaning depending on the value of the $fetchMode parameter: - * * {@link \Doctrine\DBAL\FetchMode::COLUMN}: - * Returns the indicated 0-indexed column. - * * {@link \Doctrine\DBAL\FetchMode::CUSTOM_OBJECT}: - * Returns instances of the specified class, mapping the columns of each row - * to named properties in the class. - * * \PDO::FETCH_FUNC: Returns the results of calling the specified function, using each row's - * columns as parameters in the call. - * @param mixed[]|null $ctorArgs Controls how the next row will be returned to the caller. - * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants, - * defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}. + * @param int|null $fetchMode Controls how the next row will be returned to the caller. + * The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants, + * defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}. + * @param mixed[] ...$args Optional mode-specific arguments. Supported modes: + * * {@link \Doctrine\DBAL\FetchMode::COLUMN} + * 1. The 0-indexed column to be returned. + * * {@link \Doctrine\DBAL\FetchMode::CUSTOM_OBJECT} + * 1. The classname of the object to be created, + * 2. Array of constructor arguments * * @return mixed[] */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null); + public function fetchAll($fetchMode = null, ...$args); /** * Returns a single column from the next row of a result set or FALSE if there are no more rows. diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php index 00295a2af1a..8cd59dee7d4 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereConnection.php @@ -3,12 +3,10 @@ namespace Doctrine\DBAL\Driver\SQLAnywhere; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use function assert; -use function func_get_args; -use function is_float; -use function is_int; use function is_resource; use function is_string; use function sasql_affected_rows; @@ -68,13 +66,11 @@ public function __construct($dsn, $persistent = false) * * @throws SQLAnywhereException */ - public function beginTransaction() + public function beginTransaction() : void { if (! sasql_set_option($this->connection, 'auto_commit', 'off')) { throw SQLAnywhereException::fromSQLAnywhereError($this->connection); } - - return true; } /** @@ -82,15 +78,13 @@ public function beginTransaction() * * @throws SQLAnywhereException */ - public function commit() + public function commit() : void { if (! sasql_commit($this->connection)) { throw SQLAnywhereException::fromSQLAnywhereError($this->connection); } $this->endTransaction(); - - return true; } /** @@ -112,7 +106,7 @@ public function errorInfo() /** * {@inheritdoc} */ - public function exec($statement) + public function exec(string $statement) : int { if (sasql_real_query($this->connection, $statement) === false) { throw SQLAnywhereException::fromSQLAnywhereError($this->connection); @@ -136,31 +130,45 @@ public function getServerVersion() /** * {@inheritdoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { if ($name === null) { - return sasql_insert_id($this->connection); + $result = sasql_insert_id($this->connection); + + if ($result === false) { + throw SQLAnywhereException::fromSQLAnywhereError($this->connection); + } + + if ($result === 0) { + throw SQLAnywhereException::noInsertId(); + } + + return (string) $result; } - return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + $result = $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn(); + + if ($result === false) { + throw SQLAnywhereException::noSuchSequence($name); + } + + return (string) $result; } /** * {@inheritdoc} */ - public function prepare($prepareString) + public function prepare(string $sql) : DriverStatement { - return new SQLAnywhereStatement($this->connection, $prepareString); + return new SQLAnywhereStatement($this->connection, $sql); } /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $stmt = $this->prepare($args[0]); - + $stmt = $this->prepare($sql); $stmt->execute(); return $stmt; @@ -169,12 +177,8 @@ public function query() /** * {@inheritdoc} */ - public function quote($input, $type = ParameterType::STRING) + public function quote(string $input) : string { - if (is_int($input) || is_float($input)) { - return $input; - } - return "'" . sasql_escape_string($this->connection, $input) . "'"; } @@ -191,15 +195,13 @@ public function requiresQueryForServerVersion() * * @throws SQLAnywhereException */ - public function rollBack() + public function rollBack() : void { if (! sasql_rollback($this->connection)) { throw SQLAnywhereException::fromSQLAnywhereError($this->connection); } $this->endTransaction(); - - return true; } /** diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index 0ed2d454624..3edcaea0d8c 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -7,14 +7,13 @@ use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use IteratorAggregate; -use PDO; use ReflectionClass; use ReflectionObject; use stdClass; use const SASQL_BOTH; use function array_key_exists; +use function count; use function func_get_args; -use function func_num_args; use function gettype; use function is_array; use function is_numeric; @@ -192,7 +191,7 @@ public function execute($params = null) * * @throws SQLAnywhereException */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { if (! is_resource($this->result)) { return false; @@ -214,10 +213,9 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX $className = $this->defaultFetchClass; $ctorArgs = $this->defaultFetchClassCtorArgs; - if (func_num_args() >= 2) { - $args = func_get_args(); - $className = $args[1]; - $ctorArgs = $args[2] ?? []; + if (count($args) > 0) { + $className = $args[0]; + $ctorArgs = $args[1] ?? []; } $result = sasql_fetch_object($this->result); @@ -242,25 +240,25 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $rows = []; switch ($fetchMode) { case FetchMode::CUSTOM_OBJECT: - while ($row = $this->fetch(...func_get_args())) { + while (($row = $this->fetch(...func_get_args())) !== false) { $rows[] = $row; } break; case FetchMode::COLUMN: - while ($row = $this->fetchColumn()) { + while (($row = $this->fetchColumn()) !== false) { $rows[] = $row; } break; default: - while ($row = $this->fetch($fetchMode)) { + while (($row = $this->fetch($fetchMode)) !== false) { $rows[] = $row; } } @@ -293,7 +291,7 @@ public function getIterator() /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { return sasql_stmt_affected_rows($this->stmt); } @@ -301,11 +299,19 @@ public function rowCount() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + $this->defaultFetchMode = $fetchMode; + + if (isset($args[0])) { + $this->defaultFetchClass = $args[0]; + } + + if (! isset($args[1])) { + return; + } + + $this->defaultFetchClassCtorArgs = (array) $args[1]; } /** diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php index f1f9ddd1d79..4d6fc0421d4 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvConnection.php @@ -3,13 +3,10 @@ namespace Doctrine\DBAL\Driver\SQLSrv; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\ResultStatement; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; -use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use const SQLSRV_ERR_ERRORS; -use function func_get_args; -use function is_float; -use function is_int; -use function sprintf; use function sqlsrv_begin_transaction; use function sqlsrv_commit; use function sqlsrv_configure; @@ -72,7 +69,7 @@ public function requiresQueryForServerVersion() /** * {@inheritDoc} */ - public function prepare($sql) + public function prepare(string $sql) : DriverStatement { return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId); } @@ -80,10 +77,8 @@ public function prepare($sql) /** * {@inheritDoc} */ - public function query() + public function query(string $sql) : ResultStatement { - $args = func_get_args(); - $sql = $args[0]; $stmt = $this->prepare($sql); $stmt->execute(); @@ -93,21 +88,15 @@ public function query() /** * {@inheritDoc} */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value) : string { - if (is_int($value)) { - return $value; - } elseif (is_float($value)) { - return sprintf('%F', $value); - } - return "'" . str_replace("'", "''", $value) . "'"; } /** * {@inheritDoc} */ - public function exec($statement) + public function exec(string $statement) : int { $stmt = sqlsrv_query($this->conn, $statement); @@ -121,7 +110,7 @@ public function exec($statement) /** * {@inheritDoc} */ - public function lastInsertId($name = null) + public function lastInsertId(?string $name = null) : string { if ($name !== null) { $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?'); @@ -130,13 +119,25 @@ public function lastInsertId($name = null) $stmt = $this->query('SELECT @@IDENTITY'); } - return $stmt->fetchColumn(); + $result = $stmt->fetchColumn(); + + if ($name !== null) { + if ($result === false) { + throw SQLSrvException::noSuchSequence($name); + } + } else { + if ($result === null) { + throw SQLSrvException::noInsertId(); + } + } + + return (string) $result; } /** * {@inheritDoc} */ - public function beginTransaction() + public function beginTransaction() : void { if (! sqlsrv_begin_transaction($this->conn)) { throw SQLSrvException::fromSqlSrvErrors(); @@ -146,7 +147,7 @@ public function beginTransaction() /** * {@inheritDoc} */ - public function commit() + public function commit() : void { if (! sqlsrv_commit($this->conn)) { throw SQLSrvException::fromSqlSrvErrors(); @@ -156,7 +157,7 @@ public function commit() /** * {@inheritDoc} */ - public function rollBack() + public function rollBack() : void { if (! sqlsrv_rollback($this->conn)) { throw SQLSrvException::fromSqlSrvErrors(); diff --git a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php index 147ef76e1a0..1b2e38049bd 100644 --- a/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLSrv/SQLSrvStatement.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use IteratorAggregate; -use PDO; use const SQLSRV_ENC_BINARY; use const SQLSRV_ERR_ERRORS; use const SQLSRV_FETCH_ASSOC; @@ -16,7 +15,6 @@ use const SQLSRV_PARAM_IN; use function array_key_exists; use function count; -use function func_get_args; use function in_array; use function is_numeric; use function sqlsrv_errors; @@ -302,11 +300,17 @@ private function prepare() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { - $this->defaultFetchMode = $fetchMode; - $this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass; - $this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs; + $this->defaultFetchMode = $fetchMode; + + if (isset($args[0])) { + $this->defaultFetchClass = $args[0]; + } + + if (isset($args[1])) { + $this->defaultFetchClassCtorArgs = (array) $args[1]; + } return true; } @@ -324,7 +328,7 @@ public function getIterator() * * @throws SQLSrvException */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { // do not try fetching from the statement if it's not expected to contain result // in order to prevent exceptional situation @@ -332,7 +336,6 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX return false; } - $args = func_get_args(); $fetchMode = $fetchMode ?: $this->defaultFetchMode; if ($fetchMode === FetchMode::COLUMN) { @@ -347,9 +350,9 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX $className = $this->defaultFetchClass; $ctorArgs = $this->defaultFetchClassCtorArgs; - if (count($args) >= 2) { - $className = $args[1]; - $ctorArgs = $args[2] ?? []; + if (count($args) > 0) { + $className = $args[0]; + $ctorArgs = $args[1] ?? []; } return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false; @@ -361,13 +364,13 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $rows = []; switch ($fetchMode) { case FetchMode::CUSTOM_OBJECT: - while (($row = $this->fetch(...func_get_args())) !== false) { + while (($row = $this->fetch($fetchMode, ...$args)) !== false) { $rows[] = $row; } break; @@ -404,7 +407,7 @@ public function fetchColumn($columnIndex = 0) /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { return sqlsrv_rows_affected($this->stmt); } diff --git a/lib/Doctrine/DBAL/Driver/Statement.php b/lib/Doctrine/DBAL/Driver/Statement.php index a3ea74a4df5..b859740bd6b 100644 --- a/lib/Doctrine/DBAL/Driver/Statement.php +++ b/lib/Doctrine/DBAL/Driver/Statement.php @@ -49,8 +49,7 @@ public function bindValue($param, $value, $type = ParameterType::STRING); * question mark placeholders, this will be the 1-indexed position of the parameter. * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. * @param int|null $type Explicit data type for the parameter using the {@link \Doctrine\DBAL\ParameterType} - * constants. To return an INOUT parameter from a stored procedure, use the bitwise - * OR operator to set the PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter. + * constants. * @param int|null $length You must specify maxlength when using an OUT bind * so that PHP allocates enough memory to hold the returned value. * @@ -101,5 +100,5 @@ public function execute($params = null); * * @return int The number of rows. */ - public function rowCount(); + public function rowCount() : int; } diff --git a/lib/Doctrine/DBAL/DriverManager.php b/lib/Doctrine/DBAL/DriverManager.php index 658123199c0..2f599015d62 100644 --- a/lib/Doctrine/DBAL/DriverManager.php +++ b/lib/Doctrine/DBAL/DriverManager.php @@ -3,7 +3,6 @@ namespace Doctrine\DBAL; use Doctrine\Common\EventManager; -use Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver as DrizzlePDOMySQLDriver; use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver; use Doctrine\DBAL\Driver\OCI8\Driver as OCI8Driver; @@ -42,17 +41,16 @@ final class DriverManager * @var string[] */ private static $_driverMap = [ - 'pdo_mysql' => PDOMySQLDriver::class, - 'pdo_sqlite' => PDOSQLiteDriver::class, - 'pdo_pgsql' => PDOPgSQLDriver::class, - 'pdo_oci' => PDOOCIDriver::class, - 'oci8' => OCI8Driver::class, - 'ibm_db2' => DB2Driver::class, - 'pdo_sqlsrv' => PDOSQLSrvDriver::class, - 'mysqli' => MySQLiDriver::class, - 'drizzle_pdo_mysql' => DrizzlePDOMySQLDriver::class, - 'sqlanywhere' => SQLAnywhereDriver::class, - 'sqlsrv' => SQLSrvDriver::class, + 'pdo_mysql' => PDOMySQLDriver::class, + 'pdo_sqlite' => PDOSQLiteDriver::class, + 'pdo_pgsql' => PDOPgSQLDriver::class, + 'pdo_oci' => PDOOCIDriver::class, + 'oci8' => OCI8Driver::class, + 'ibm_db2' => DB2Driver::class, + 'pdo_sqlsrv' => PDOSQLSrvDriver::class, + 'mysqli' => MySQLiDriver::class, + 'sqlanywhere' => SQLAnywhereDriver::class, + 'sqlsrv' => SQLSrvDriver::class, ]; /** @@ -98,7 +96,6 @@ private function __construct() * sqlanywhere * sqlsrv * ibm_db2 (unstable) - * drizzle_pdo_mysql * * OR 'driverClass' that contains the full class name (with namespace) of the * driver class to instantiate. diff --git a/lib/Doctrine/DBAL/FetchMode.php b/lib/Doctrine/DBAL/FetchMode.php index b3fe42f4ceb..65af014f1e0 100644 --- a/lib/Doctrine/DBAL/FetchMode.php +++ b/lib/Doctrine/DBAL/FetchMode.php @@ -2,8 +2,6 @@ namespace Doctrine\DBAL; -use PDO; - /** * Contains statement fetch modes. */ @@ -17,7 +15,7 @@ final class FetchMode * * @see \PDO::FETCH_ASSOC */ - public const ASSOCIATIVE = PDO::FETCH_ASSOC; + public const ASSOCIATIVE = 2; /** * Specifies that the fetch method shall return each row as an array indexed @@ -26,7 +24,7 @@ final class FetchMode * * @see \PDO::FETCH_NUM */ - public const NUMERIC = PDO::FETCH_NUM; + public const NUMERIC = 3; /** * Specifies that the fetch method shall return each row as an array indexed @@ -35,7 +33,7 @@ final class FetchMode * * @see \PDO::FETCH_BOTH */ - public const MIXED = PDO::FETCH_BOTH; + public const MIXED = 4; /** * Specifies that the fetch method shall return each row as an object with @@ -44,7 +42,7 @@ final class FetchMode * * @see \PDO::FETCH_OBJ */ - public const STANDARD_OBJECT = PDO::FETCH_OBJ; + public const STANDARD_OBJECT = 5; /** * Specifies that the fetch method shall return only a single requested @@ -52,7 +50,7 @@ final class FetchMode * * @see \PDO::FETCH_COLUMN */ - public const COLUMN = PDO::FETCH_COLUMN; + public const COLUMN = 7; /** * Specifies that the fetch method shall return a new instance of the @@ -60,7 +58,7 @@ final class FetchMode * * @see \PDO::FETCH_CLASS */ - public const CUSTOM_OBJECT = PDO::FETCH_CLASS; + public const CUSTOM_OBJECT = 8; /** * This class cannot be instantiated. diff --git a/lib/Doctrine/DBAL/Logging/DebugStack.php b/lib/Doctrine/DBAL/Logging/DebugStack.php index e1ccaad6ba0..bd1358f321d 100644 --- a/lib/Doctrine/DBAL/Logging/DebugStack.php +++ b/lib/Doctrine/DBAL/Logging/DebugStack.php @@ -7,7 +7,7 @@ /** * Includes executed SQLs in a Debug Stack. */ -class DebugStack implements SQLLogger +final class DebugStack implements SQLLogger { /** * Executed SQL queries. @@ -32,7 +32,7 @@ class DebugStack implements SQLLogger /** * {@inheritdoc} */ - public function startQuery($sql, ?array $params = null, ?array $types = null) + public function startQuery(string $sql, array $params = [], array $types = []) : void { if (! $this->enabled) { return; @@ -45,7 +45,7 @@ public function startQuery($sql, ?array $params = null, ?array $types = null) /** * {@inheritdoc} */ - public function stopQuery() + public function stopQuery() : void { if (! $this->enabled) { return; diff --git a/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php b/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php index 657abb4d378..cfca9dd7c90 100644 --- a/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php +++ b/lib/Doctrine/DBAL/Logging/EchoSQLLogger.php @@ -8,12 +8,12 @@ /** * A SQL logger that logs to the standard output using echo/var_dump. */ -class EchoSQLLogger implements SQLLogger +final class EchoSQLLogger implements SQLLogger { /** * {@inheritdoc} */ - public function startQuery($sql, ?array $params = null, ?array $types = null) + public function startQuery(string $sql, array $params = [], array $types = []) : void { echo $sql . PHP_EOL; @@ -31,7 +31,7 @@ public function startQuery($sql, ?array $params = null, ?array $types = null) /** * {@inheritdoc} */ - public function stopQuery() + public function stopQuery() : void { } } diff --git a/lib/Doctrine/DBAL/Logging/LoggerChain.php b/lib/Doctrine/DBAL/Logging/LoggerChain.php index 2b5404b25c7..226faae83da 100644 --- a/lib/Doctrine/DBAL/Logging/LoggerChain.php +++ b/lib/Doctrine/DBAL/Logging/LoggerChain.php @@ -5,17 +5,15 @@ /** * Chains multiple SQLLogger. */ -class LoggerChain implements SQLLogger +final class LoggerChain implements SQLLogger { /** @var SQLLogger[] */ private $loggers = []; /** * Adds a logger in the chain. - * - * @return void */ - public function addLogger(SQLLogger $logger) + public function addLogger(SQLLogger $logger) : void { $this->loggers[] = $logger; } @@ -23,7 +21,7 @@ public function addLogger(SQLLogger $logger) /** * {@inheritdoc} */ - public function startQuery($sql, ?array $params = null, ?array $types = null) + public function startQuery(string $sql, array $params = [], array $types = []) : void { foreach ($this->loggers as $logger) { $logger->startQuery($sql, $params, $types); @@ -33,7 +31,7 @@ public function startQuery($sql, ?array $params = null, ?array $types = null) /** * {@inheritdoc} */ - public function stopQuery() + public function stopQuery() : void { foreach ($this->loggers as $logger) { $logger->stopQuery(); diff --git a/lib/Doctrine/DBAL/Logging/NullLogger.php b/lib/Doctrine/DBAL/Logging/NullLogger.php new file mode 100644 index 00000000000..3a925f13ca0 --- /dev/null +++ b/lib/Doctrine/DBAL/Logging/NullLogger.php @@ -0,0 +1,25 @@ +getBinaryMaxLength(); - - if ($field['length'] > $maxLength) { - if ($maxLength > 0) { - @trigger_error(sprintf( - 'Binary field length %d is greater than supported by the platform (%d). Reduce the field length or use a BLOB field instead.', - $field['length'], - $maxLength - ), E_USER_DEPRECATED); - } - - return $this->getBlobTypeDeclarationSQL($field); - } - return $this->getBinaryTypeDeclarationSQLSnippet($field['length'], $fixed); } @@ -631,20 +614,6 @@ public function getRegexpExpression() throw DBALException::notSupported(__METHOD__); } - /** - * Returns the global unique identifier expression. - * - * @deprecated Use application-generated UUIDs instead - * - * @return string - * - * @throws DBALException If not supported on this platform. - */ - public function getGuidExpression() - { - throw DBALException::notSupported(__METHOD__); - } - /** * Returns the SQL snippet to get the average value of a column. * @@ -1534,15 +1503,30 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE $options['indexes'] = []; $options['primary'] = []; - if (($createFlags&self::CREATE_INDEXES) > 0) { + if (($createFlags & self::CREATE_INDEXES) > 0) { foreach ($table->getIndexes() as $index) { /** @var $index Index */ - if ($index->isPrimary()) { - $options['primary'] = $index->getQuotedColumns($this); - $options['primary_index'] = $index; - } else { + if (! $index->isPrimary()) { $options['indexes'][$index->getQuotedName($this)] = $index; + + continue; } + + $options['primary'] = $index->getQuotedColumns($this); + $options['primary_index'] = $index; + } + + foreach ($table->getUniqueConstraints() as $uniqueConstraint) { + /** @var UniqueConstraint $uniqueConstraint */ + $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint; + } + } + + if (($createFlags & self::CREATE_FOREIGNKEYS) > 0) { + $options['foreignKeys'] = []; + + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; } } @@ -1551,7 +1535,6 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE foreach ($table->getColumns() as $column) { /** @var Column $column */ - if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn)) { $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); @@ -1579,13 +1562,6 @@ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDE $columns[$columnData['name']] = $columnData; } - if (($createFlags&self::CREATE_FOREIGNKEYS) > 0) { - $options['foreignKeys'] = []; - foreach ($table->getForeignKeys() as $fkConstraint) { - $options['foreignKeys'][] = $fkConstraint; - } - } - if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); @@ -2330,25 +2306,33 @@ public function getCheckDeclarationSQL(array $definition) * Obtains DBMS specific SQL code portion needed to set a unique * constraint declaration to be used in statements like CREATE TABLE. * - * @param string $name The name of the unique constraint. - * @param Index $index The index definition. + * @param string $name The name of the unique constraint. + * @param UniqueConstraint $constraint The unique constraint definition. * * @return string DBMS specific SQL code portion needed to set a constraint. * * @throws InvalidArgumentException */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) + public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint) { - $columns = $index->getColumns(); + $columns = $constraint->getColumns(); $name = new Identifier($name); if (count($columns) === 0) { throw new InvalidArgumentException("Incomplete definition. 'columns' required."); } - return 'CONSTRAINT ' . $name->getQuotedName($this) . ' UNIQUE (' - . $this->getIndexFieldDeclarationListSQL($index) - . ')' . $this->getPartialIndexSQL($index); + $flags = ['UNIQUE']; + + if ($constraint->hasFlag('clustered')) { + $flags[] = 'CLUSTERED'; + } + + $constraintName = $name->getQuotedName($this); + $constraintName = ! empty($constraintName) ? $constraintName . ' ' : ''; + $columnListNames = $this->getIndexFieldDeclarationListSQL($columns); + + return sprintf('CONSTRAINT %s%s (%s)', $constraintName, implode(' ', $flags), $columnListNames); } /** @@ -3044,6 +3028,26 @@ public function usesSequenceEmulatedIdentityColumns() return false; } + /** + * Gets the sequence name prefix based on table information. + * + * @param string $tableName + * @param string|null $schemaName + * + * @return string + */ + public function getSequencePrefix($tableName, $schemaName = null) + { + if (! $schemaName) { + return $tableName; + } + + // Prepend the schema name to the table name if there is one + return ! $this->supportsSchemas() && $this->canEmulateSchemas() + ? $schemaName . '__' . $tableName + : $schemaName . '.' . $tableName; + } + /** * Returns the name of the sequence for a particular identity column in a particular table. * @@ -3334,22 +3338,10 @@ public function getTimeFormatString() /** * Adds an driver-specific LIMIT clause to the query. * - * @param string $query - * @param int|null $limit - * @param int|null $offset - * - * @return string - * * @throws DBALException */ - final public function modifyLimitQuery($query, $limit, $offset = null) + final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0) : string { - if ($limit !== null) { - $limit = (int) $limit; - } - - $offset = (int) $offset; - if ($offset < 0) { throw new DBALException(sprintf( 'Offset must be a positive integer or zero, %d given', @@ -3369,21 +3361,15 @@ final public function modifyLimitQuery($query, $limit, $offset = null) /** * Adds an platform-specific LIMIT clause to the query. - * - * @param string $query - * @param int|null $limit - * @param int|null $offset - * - * @return string */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { if ($limit !== null) { - $query .= ' LIMIT ' . $limit; + $query .= sprintf(' LIMIT %d', $limit); } if ($offset > 0) { - $query .= ' OFFSET ' . $offset; + $query .= sprintf(' OFFSET %d', $offset); } return $query; @@ -3467,13 +3453,9 @@ public function getTruncateTableSQL($tableName, $cascade = false) /** * This is for test reasons, many vendors have special requirements for dummy statements. - * - * @return string */ - public function getDummySelectSQL() + public function getDummySelectSQL(string $expression = '1') : string { - $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; - return sprintf('SELECT %s', $expression); } diff --git a/lib/Doctrine/DBAL/Platforms/DB2Platform.php b/lib/Doctrine/DBAL/Platforms/DB2Platform.php index 605122115d4..5d85dfec485 100644 --- a/lib/Doctrine/DBAL/Platforms/DB2Platform.php +++ b/lib/Doctrine/DBAL/Platforms/DB2Platform.php @@ -13,8 +13,6 @@ use function count; use function current; use function explode; -use function func_get_arg; -use function func_num_args; use function implode; use function sprintf; use function strpos; @@ -74,21 +72,21 @@ public function getBlobTypeDeclarationSQL(array $field) public function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'smallint' => 'smallint', - 'bigint' => 'bigint', - 'integer' => 'integer', - 'time' => 'time', - 'date' => 'date', - 'varchar' => 'string', - 'character' => 'string', - 'varbinary' => 'binary', - 'binary' => 'binary', - 'clob' => 'text', - 'blob' => 'blob', - 'decimal' => 'decimal', - 'double' => 'float', - 'real' => 'float', - 'timestamp' => 'datetime', + 'bigint' => 'bigint', + 'binary' => 'binary', + 'blob' => 'blob', + 'character' => 'string', + 'clob' => 'text', + 'date' => 'date', + 'decimal' => 'decimal', + 'double' => 'float', + 'integer' => 'integer', + 'real' => 'float', + 'smallint' => 'smallint', + 'time' => 'time', + 'timestamp' => 'datetime', + 'varbinary' => 'binary', + 'varchar' => 'string', ]; } @@ -782,7 +780,7 @@ public function getTemporaryTableName($tableName) /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { $where = []; @@ -867,10 +865,8 @@ public function getForUpdateSQL() /** * {@inheritDoc} */ - public function getDummySelectSQL() + public function getDummySelectSQL(string $expression = '1') : string { - $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; - return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression); } diff --git a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php b/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php deleted file mode 100644 index 2ba8c03b3b3..00000000000 --- a/lib/Doctrine/DBAL/Platforms/DrizzlePlatform.php +++ /dev/null @@ -1,623 +0,0 @@ -_getCommonIntegerTypeDeclarationSQL($field); - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) - { - $autoinc = ''; - if (! empty($columnDef['autoincrement'])) { - $autoinc = ' AUTO_INCREMENT'; - } - - return $autoinc; - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $field) - { - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($field); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $field) - { - return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($field); - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'; - } - - /** - * {@inheritdoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return 'VARBINARY(' . ($length ?: 255) . ')'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'boolean' => 'boolean', - 'varchar' => 'string', - 'varbinary' => 'binary', - 'integer' => 'integer', - 'blob' => 'blob', - 'decimal' => 'decimal', - 'datetime' => 'datetime', - 'date' => 'date', - 'time' => 'time', - 'text' => 'text', - 'timestamp' => 'datetime', - 'double' => 'float', - 'bigint' => 'bigint', - ]; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $field) - { - return 'TEXT'; - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $field) - { - return 'BLOB'; - } - - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) - { - return 'CREATE DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) - { - return 'DROP DATABASE ' . $name; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($tableName, array $columns, array $options = []) - { - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $index => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); - } - } - - // add all indexes - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index => $definition) { - $queryFields .= ', ' . $this->getIndexDeclarationSQL($index, $definition); - } - } - - // attach all primary keys - if (isset($options['primary']) && ! empty($options['primary'])) { - $keyColumns = array_unique(array_values($options['primary'])); - $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - $query = 'CREATE '; - - if (! empty($options['temporary'])) { - $query .= 'TEMPORARY '; - } - - $query .= 'TABLE ' . $tableName . ' (' . $queryFields . ') '; - $query .= $this->buildTableOptions($options); - $query .= $this->buildPartitionOptions($options); - - $sql = [$query]; - - if (isset($options['foreignKeys'])) { - foreach ((array) $options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $tableName); - } - } - - return $sql; - } - - /** - * Build SQL for table options - * - * @param mixed[] $options - * - * @return string - */ - private function buildTableOptions(array $options) - { - if (isset($options['table_options'])) { - return $options['table_options']; - } - - $tableOptions = []; - - // Collate - if (! isset($options['collate'])) { - $options['collate'] = 'utf8_unicode_ci'; - } - - $tableOptions[] = sprintf('COLLATE %s', $options['collate']); - - // Engine - if (! isset($options['engine'])) { - $options['engine'] = 'InnoDB'; - } - - $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); - - // Auto increment - if (isset($options['auto_increment'])) { - $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); - } - - // Comment - if (isset($options['comment'])) { - $comment = trim($options['comment'], " '"); - - $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($comment)); - } - - // Row format - if (isset($options['row_format'])) { - $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); - } - - return implode(' ', $tableOptions); - } - - /** - * Build SQL for partition options. - * - * @param mixed[] $options - * - * @return string - */ - private function buildPartitionOptions(array $options) - { - return isset($options['partition_options']) - ? ' ' . $options['partition_options'] - : ''; - } - - /** - * {@inheritDoc} - */ - public function getListDatabasesSQL() - { - return "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE CATALOG_NAME='LOCAL'"; - } - - /** - * {@inheritDoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\DrizzleKeywords::class; - } - - /** - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE' AND TABLE_SCHEMA=DATABASE()"; - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return 'SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE, IS_AUTO_INCREMENT, CHARACTER_MAXIMUM_LENGTH, COLUMN_DEFAULT,' . - ' NUMERIC_PRECISION, NUMERIC_SCALE, COLLATION_NAME' . - ' FROM DATA_DICTIONARY.COLUMNS' . - ' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table); - } - - /** - * {@inheritDoc} - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return 'SELECT CONSTRAINT_NAME, CONSTRAINT_COLUMNS, REFERENCED_TABLE_NAME, REFERENCED_TABLE_COLUMNS, UPDATE_RULE, DELETE_RULE' . - ' FROM DATA_DICTIONARY.FOREIGN_KEYS' . - ' WHERE CONSTRAINT_SCHEMA=' . $databaseSQL . ' AND CONSTRAINT_TABLE=' . $this->quoteStringLiteral($table); - } - - /** - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - if ($database) { - $databaseSQL = $this->quoteStringLiteral($database); - } else { - $databaseSQL = 'DATABASE()'; - } - - return "SELECT INDEX_NAME AS 'key_name', COLUMN_NAME AS 'column_name', IS_USED_IN_PRIMARY AS 'primary', IS_UNIQUE=0 AS 'non_unique'" . - ' FROM DATA_DICTIONARY.INDEX_PARTS' . - ' WHERE TABLE_SCHEMA=' . $databaseSQL . ' AND TABLE_NAME=' . $this->quoteStringLiteral($table); - } - - /** - * {@inheritDoc} - */ - public function prefersIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsInlineColumnComments() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function supportsViews() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - $indexName = $index->getQuotedName($this); - } elseif (is_string($index)) { - $indexName = $index; - } else { - throw new InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $index parameter to be string or \Doctrine\DBAL\Schema\Index.'); - } - - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException('DrizzlePlatform::getDropIndexSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); - } - - if ($index instanceof Index && $index->isPrimary()) { - // drizzle primary keys are always named "PRIMARY", - // so we cannot use them in statements because of them being keyword. - return $this->getDropPrimaryKeySQL($table); - } - - return 'DROP INDEX ' . $indexName . ' ON ' . $table; - } - - /** - * {@inheritDoc} - */ - protected function getDropPrimaryKeySQL($table) - { - return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) - { - if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] === true) { - return 'TIMESTAMP'; - } - - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $fieldDeclaration) - { - return 'TIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $fieldDeclaration) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $columnSql = []; - $queryParts = []; - - if ($diff->newName !== false) { - $queryParts[] = 'RENAME TO ' . $diff->getNewName()->getQuotedName($this); - } - - foreach ($diff->addedColumns as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columnArray = $column->toArray(); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->removedColumns as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $queryParts[] = 'DROP ' . $column->getQuotedName($this); - } - - foreach ($diff->changedColumns as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - /** @var ColumnDiff $columnDiff */ - $column = $columnDiff->column; - $columnArray = $column->toArray(); - - // Do not generate column alteration clause if type is binary and only fixed property has changed. - // Drizzle only supports binary type columns with variable length. - // Avoids unnecessary table alteration statements. - if ($columnArray['type'] instanceof BinaryType && - $columnDiff->hasChanged('fixed') && - count($columnDiff->changedProperties) === 1 - ) { - continue; - } - - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . ($columnDiff->getOldColumnName()->getQuotedName($this)) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - foreach ($diff->renamedColumns as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = new Identifier($oldColumnName); - - $columnArray = $column->toArray(); - $columnArray['comment'] = $this->getColumnComment($column); - $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' - . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnArray); - } - - $sql = []; - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(', ', $queryParts); - } - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff) - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * {@inheritDoc} - */ - public function getDropTemporaryTableSQL($table) - { - if ($table instanceof Table) { - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException('getDropTableSQL() expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.'); - } - - return 'DROP TEMPORARY TABLE ' . $table; - } - - /** - * {@inheritDoc} - */ - public function convertBooleans($item) - { - if (is_array($item)) { - foreach ($item as $key => $value) { - if (! is_bool($value) && ! is_numeric($item)) { - continue; - } - - $item[$key] = $value ? 'true' : 'false'; - } - } elseif (is_bool($item) || is_numeric($item)) { - $item = $item ? 'true' : 'false'; - } - - return $item; - } - - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) - { - if ($startPos === false) { - return 'LOCATE(' . $substr . ', ' . $str . ')'; - } - - return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; - } - - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'UUID()'; - } - - /** - * {@inheritDoc} - */ - public function getRegexpExpression() - { - return 'RLIKE'; - } -} diff --git a/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php b/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php index 8533f579d71..db577d30207 100644 --- a/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php +++ b/lib/Doctrine/DBAL/Platforms/Keywords/DB2Keywords.php @@ -27,24 +27,6 @@ protected function getKeywords() 'ALIAS', 'ALL', 'ALLOCATE', - 'DOCUMENT', - 'DOUBLE', - 'DROP', - 'DSSIZE', - 'DYNAMIC', - 'EACH', - 'LOCK', - 'LOCKMAX', - 'LOCKSIZE', - 'LONG', - 'LOOP', - 'MAINTAINED', - 'ROUND_CEILING', - 'ROUND_DOWN', - 'ROUND_FLOOR', - 'ROUND_HALF_DOWN', - 'ROUND_HALF_EVEN', - 'ROUND_HALF_UP', 'ALLOW', 'ALTER', 'AND', @@ -112,6 +94,38 @@ protected function getKeywords() 'DATABASE', 'DATAPARTITIONNAME', 'DATAPARTITIONNUM', + 'DATE', + 'DAY', + 'DAYS', + 'DB2GENERAL', + 'DB2GENRL', + 'DB2SQL', + 'DBINFO', + 'DBPARTITIONNAME', + 'DBPARTITIONNUM', + 'DEALLOCATE', + 'DECLARE', + 'DEFAULT', + 'DEFAULTS', + 'DEFINITION', + 'DELETE', + 'DENSE_RANK', + 'DENSERANK', + 'DESCRIBE', + 'DESCRIPTOR', + 'DETERMINISTIC', + 'DIAGNOSTICS', + 'DISABLE', + 'DISALLOW', + 'DISCONNECT', + 'DISTINCT', + 'DO', + 'DOCUMENT', + 'DOUBLE', + 'DROP', + 'DSSIZE', + 'DYNAMIC', + 'EACH', 'EDITPROC', 'ELSE', 'ELSEIF', @@ -179,6 +193,38 @@ protected function getKeywords() 'INSENSITIVE', 'INSERT', 'INTEGRITY', + 'INTERSECT', + 'INTO', + 'IS', + 'ISOBID', + 'ISOLATION', + 'ITERATE', + 'JAR', + 'JAVA', + 'JOIN', + 'KEEP', + 'KEY', + 'LABEL', + 'LANGUAGE', + 'LATERAL', + 'LC_CTYPE', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LINKTYPE', + 'LOCAL', + 'LOCALDATE', + 'LOCALE', + 'LOCALTIME', + 'LOCALTIMESTAMP RIGHT', + 'LOCATOR', + 'LOCATORS', + 'LOCK', + 'LOCKMAX', + 'LOCKSIZE', + 'LONG', + 'LOOP', + 'MAINTAINED', 'MATERIALIZED', 'MAXVALUE', 'MICROSECOND', @@ -246,6 +292,37 @@ protected function getKeywords() 'PROCEDURE', 'PROGRAM', 'PSID', + 'PUBLIC', + 'QUERY', + 'QUERYNO', + 'RANGE', + 'RANK', + 'READ', + 'READS', + 'RECOVERY', + 'REFERENCES', + 'REFERENCING', + 'REFRESH', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'RESET', + 'RESIGNAL', + 'RESTART', + 'RESTRICT', + 'RESULT', + 'RESULT_SET_LOCATOR WLM', + 'RETURN', + 'RETURNS', + 'REVOKE', + 'ROLE', + 'ROLLBACK', + 'ROUND_CEILING', + 'ROUND_DOWN', + 'ROUND_FLOOR', + 'ROUND_HALF_DOWN', + 'ROUND_HALF_EVEN', + 'ROUND_HALF_UP', 'ROUND_UP', 'ROUTINE', 'ROW', @@ -313,107 +390,30 @@ protected function getKeywords() 'UNIQUE', 'UNTIL', 'UPDATE', - 'DATE', - 'DAY', - 'DAYS', - 'DB2GENERAL', - 'DB2GENRL', - 'DB2SQL', - 'DBINFO', - 'DBPARTITIONNAME', - 'DBPARTITIONNUM', - 'DEALLOCATE', - 'DECLARE', - 'DEFAULT', - 'DEFAULTS', - 'DEFINITION', - 'DELETE', - 'DENSE_RANK', - 'DENSERANK', - 'DESCRIBE', - 'DESCRIPTOR', - 'DETERMINISTIC', - 'DIAGNOSTICS', - 'DISABLE', - 'DISALLOW', - 'DISCONNECT', - 'DISTINCT', - 'DO', - 'INTERSECT', - 'PUBLIC', 'USAGE', - 'INTO', - 'QUERY', 'USER', - 'IS', - 'QUERYNO', 'USING', - 'ISOBID', - 'RANGE', 'VALIDPROC', - 'ISOLATION', - 'RANK', 'VALUE', - 'ITERATE', - 'READ', 'VALUES', - 'JAR', - 'READS', 'VARIABLE', - 'JAVA', - 'RECOVERY', 'VARIANT', - 'JOIN', - 'REFERENCES', 'VCAT', - 'KEEP', - 'REFERENCING', 'VERSION', - 'KEY', - 'REFRESH', 'VIEW', - 'LABEL', - 'RELEASE', 'VOLATILE', - 'LANGUAGE', - 'RENAME', 'VOLUMES', - 'LATERAL', - 'REPEAT', 'WHEN', - 'LC_CTYPE', - 'RESET', 'WHENEVER', - 'LEAVE', - 'RESIGNAL', 'WHERE', - 'LEFT', - 'RESTART', 'WHILE', - 'LIKE', - 'RESTRICT', 'WITH', - 'LINKTYPE', - 'RESULT', 'WITHOUT', - 'LOCAL', - 'RESULT_SET_LOCATOR WLM', - 'LOCALDATE', - 'RETURN', 'WRITE', - 'LOCALE', - 'RETURNS', 'XMLELEMENT', - 'LOCALTIME', - 'REVOKE', 'XMLEXISTS', - 'LOCALTIMESTAMP RIGHT', 'XMLNAMESPACES', - 'LOCATOR', - 'ROLE', 'YEAR', - 'LOCATORS', - 'ROLLBACK', 'YEARS', ]; } diff --git a/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php b/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php deleted file mode 100644 index 70f69f44b0b..00000000000 --- a/lib/Doctrine/DBAL/Platforms/Keywords/DrizzleKeywords.php +++ /dev/null @@ -1,326 +0,0 @@ -getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $index => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); + foreach ($options['uniqueConstraints'] as $name => $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition); } } @@ -1025,36 +1015,36 @@ public function getReadLockSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'tinyint' => 'boolean', - 'smallint' => 'smallint', - 'mediumint' => 'integer', - 'int' => 'integer', - 'integer' => 'integer', - 'bigint' => 'bigint', - 'tinytext' => 'text', - 'mediumtext' => 'text', - 'longtext' => 'text', - 'text' => 'text', - 'varchar' => 'string', - 'string' => 'string', - 'char' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'time' => 'time', - 'float' => 'float', - 'double' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', - 'numeric' => 'decimal', - 'year' => 'date', - 'longblob' => 'blob', - 'blob' => 'blob', - 'mediumblob' => 'blob', - 'tinyblob' => 'blob', - 'binary' => 'binary', - 'varbinary' => 'binary', - 'set' => 'simple_array', + 'bigint' => 'bigint', + 'binary' => 'binary', + 'blob' => 'blob', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'decimal' => 'decimal', + 'double' => 'float', + 'float' => 'float', + 'int' => 'integer', + 'integer' => 'integer', + 'longblob' => 'blob', + 'longtext' => 'text', + 'mediumblob' => 'blob', + 'mediumint' => 'integer', + 'mediumtext' => 'text', + 'numeric' => 'decimal', + 'real' => 'float', + 'set' => 'simple_array', + 'smallint' => 'smallint', + 'string' => 'string', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'tinyblob' => 'blob', + 'tinyint' => 'boolean', + 'tinytext' => 'text', + 'varbinary' => 'binary', + 'varchar' => 'string', + 'year' => 'date', ]; } diff --git a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php index 1d249e9aa10..36990ed1dcb 100644 --- a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -16,8 +16,6 @@ use function array_merge; use function count; use function explode; -use function func_get_arg; -use function func_num_args; use function implode; use function preg_match; use function sprintf; @@ -85,16 +83,6 @@ public function getLocateExpression($str, $substr, $startPos = false) return 'INSTR(' . $str . ', ' . $substr . ', ' . $startPos . ')'; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'SYS_GUID()'; - } - /** * {@inheritdoc} */ @@ -373,11 +361,11 @@ public function getListSequencesSQL($database) /** * {@inheritDoc} */ - protected function _getCreateTableSQL($table, array $columns, array $options = []) + protected function _getCreateTableSQL($tableName, array $columns, array $options = []) { $indexes = $options['indexes'] ?? []; $options['indexes'] = []; - $sql = parent::_getCreateTableSQL($table, $columns, $options); + $sql = parent::_getCreateTableSQL($tableName, $columns, $options); foreach ($columns as $name => $column) { if (isset($column['sequence'])) { @@ -389,12 +377,12 @@ protected function _getCreateTableSQL($table, array $columns, array $options = [ continue; } - $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $table)); + $sql = array_merge($sql, $this->getCreateAutoincrementSql($name, $tableName)); } if (isset($indexes) && ! empty($indexes)) { foreach ($indexes as $index) { - $sql[] = $this->getCreateIndexSQL($index, $table); + $sql[] = $this->getCreateIndexSQL($index, $tableName); } } @@ -981,7 +969,7 @@ public function getName() /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { if ($limit === null && $offset <= 0) { return $query; @@ -1112,10 +1100,8 @@ public function getTruncateTableSQL($tableName, $cascade = false) /** * {@inheritDoc} */ - public function getDummySelectSQL() + public function getDummySelectSQL(string $expression = '1') : string { - $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; - return sprintf('SELECT %s FROM DUAL', $expression); } @@ -1125,29 +1111,29 @@ public function getDummySelectSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'integer' => 'integer', - 'number' => 'integer', - 'pls_integer' => 'boolean', - 'binary_integer' => 'boolean', - 'varchar' => 'string', - 'varchar2' => 'string', - 'nvarchar2' => 'string', - 'char' => 'string', - 'nchar' => 'string', - 'date' => 'date', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'float' => 'float', - 'binary_float' => 'float', - 'binary_double' => 'float', - 'long' => 'string', - 'clob' => 'text', - 'nclob' => 'text', - 'raw' => 'binary', - 'long raw' => 'blob', - 'rowid' => 'string', - 'urowid' => 'string', - 'blob' => 'blob', + 'binary_double' => 'float', + 'binary_float' => 'float', + 'binary_integer' => 'boolean', + 'blob' => 'blob', + 'char' => 'string', + 'clob' => 'text', + 'date' => 'date', + 'float' => 'float', + 'integer' => 'integer', + 'long' => 'string', + 'long raw' => 'blob', + 'nchar' => 'string', + 'nclob' => 'text', + 'number' => 'integer', + 'nvarchar2' => 'string', + 'pls_integer' => 'boolean', + 'raw' => 'binary', + 'rowid' => 'string', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'urowid' => 'string', + 'varchar' => 'string', + 'varchar2' => 'string', ]; } diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php b/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php deleted file mode 100644 index f558409831c..00000000000 --- a/lib/Doctrine/DBAL/Platforms/PostgreSQL91Platform.php +++ /dev/null @@ -1,46 +0,0 @@ -quoteSingleIdentifier($collation); - } - - /** - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $sql = parent::getListTableColumnsSQL($table, $database); - $parts = explode('AS complete_type,', $sql, 2); - - return $parts[0] . 'AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation,' . $parts[1]; - } -} diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php b/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php deleted file mode 100644 index b302c0ceb83..00000000000 --- a/lib/Doctrine/DBAL/Platforms/PostgreSQL92Platform.php +++ /dev/null @@ -1,69 +0,0 @@ -doctrineTypeMapping['json'] = Type::JSON; - } - - /** - * {@inheritdoc} - */ - public function getCloseActiveDatabaseConnectionsSQL($database) - { - return sprintf( - 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = %s', - $this->quoteStringLiteral($database) - ); - } -} diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php b/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php index 9db0ecbbd2a..8cb65542e3b 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSQL94Platform.php @@ -7,7 +7,7 @@ /** * Provides the behavior, features and SQL dialect of the PostgreSQL 9.4 database platform. */ -class PostgreSQL94Platform extends PostgreSQL92Platform +class PostgreSQL94Platform extends PostgreSqlPlatform { /** * {@inheritdoc} diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php index cb4603f5199..c9389a7aa7a 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -33,7 +33,7 @@ use function trim; /** - * PostgreSqlPlatform. + * Provides the behavior, features and SQL dialect of the PostgreSQL 9.4+ database platform. * * @todo Rename: PostgreSQLPlatform */ @@ -392,6 +392,7 @@ public function getListTableColumnsSQL($table, $database = null) quote_ident(a.attname) AS field, t.typname AS type, format_type(a.atttypid, a.atttypmod) AS complete_type, + (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, @@ -452,7 +453,7 @@ public function getDisallowDatabaseConnectionsSQL($database) */ public function getCloseActiveDatabaseConnectionsSQL($database) { - return 'SELECT pg_terminate_backend(procpid) FROM pg_stat_activity WHERE datname = ' + return 'SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = ' . $this->quoteStringLiteral($database); } @@ -967,6 +968,10 @@ public function getBigIntTypeDeclarationSQL(array $field) */ public function getSmallIntTypeDeclarationSQL(array $field) { + if (! empty($field['autoincrement'])) { + return 'SMALLSERIAL'; + } + return 'SMALLINT'; } @@ -1010,16 +1015,6 @@ public function getTimeTypeDeclarationSQL(array $fieldDeclaration) return 'TIME(0) WITHOUT TIME ZONE'; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'UUID_GENERATE_V4()'; - } - /** * {@inheritDoc} */ @@ -1116,45 +1111,46 @@ public function getReadLockSQL() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'smallint' => 'smallint', - 'int2' => 'smallint', - 'serial' => 'integer', - 'serial4' => 'integer', - 'int' => 'integer', - 'int4' => 'integer', - 'integer' => 'integer', - 'bigserial' => 'bigint', - 'serial8' => 'bigint', - 'bigint' => 'bigint', - 'int8' => 'bigint', - 'bool' => 'boolean', - 'boolean' => 'boolean', - 'text' => 'text', - 'tsvector' => 'text', - 'varchar' => 'string', - 'interval' => 'string', - '_varchar' => 'string', - 'char' => 'string', - 'bpchar' => 'string', - 'inet' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'timestamptz' => 'datetimetz', - 'time' => 'time', - 'timetz' => 'time', - 'float' => 'float', - 'float4' => 'float', - 'float8' => 'float', - 'double' => 'float', + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'bool' => 'boolean', + 'boolean' => 'boolean', + 'bpchar' => 'string', + 'bytea' => 'blob', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'decimal' => 'decimal', + 'double' => 'float', 'double precision' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', - 'money' => 'decimal', - 'numeric' => 'decimal', - 'year' => 'date', - 'uuid' => 'guid', - 'bytea' => 'blob', + 'float' => 'float', + 'float4' => 'float', + 'float8' => 'float', + 'inet' => 'string', + 'int' => 'integer', + 'int2' => 'smallint', + 'int4' => 'integer', + 'int8' => 'bigint', + 'integer' => 'integer', + 'interval' => 'string', + 'json' => Type::JSON, + 'money' => 'decimal', + 'numeric' => 'decimal', + 'serial' => 'integer', + 'serial4' => 'integer', + 'serial8' => 'bigint', + 'real' => 'float', + 'smallint' => 'smallint', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'timestamptz' => 'datetimetz', + 'timetz' => 'time', + 'tsvector' => 'text', + 'uuid' => 'guid', + 'varchar' => 'string', + 'year' => 'date', + '_varchar' => 'string', ]; } @@ -1182,6 +1178,14 @@ public function getBinaryDefaultLength() return 0; } + /** + * {@inheritdoc} + */ + public function hasNativeJsonType() + { + return true; + } + /** * {@inheritDoc} */ @@ -1210,6 +1214,30 @@ public function getDefaultValueDeclarationSQL($field) return parent::getDefaultValueDeclarationSQL($field); } + /** + * {@inheritdoc} + */ + public function supportsColumnCollation() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getColumnCollationDeclarationSQL($collation) + { + return 'COLLATE ' . $this->quoteSingleIdentifier($collation); + } + + /** + * {@inheritdoc} + */ + public function getJsonTypeDeclarationSQL(array $field) + { + return 'JSON'; + } + /** * @param mixed[] $field */ diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php deleted file mode 100644 index a46ae9352c4..00000000000 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywhere11Platform.php +++ /dev/null @@ -1,26 +0,0 @@ -getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize() . - ' START WITH ' . $sequence->getInitialValue() . - ' MINVALUE ' . $sequence->getInitialValue(); - } - - /** - * {@inheritdoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) - { - return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . - ' INCREMENT BY ' . $sequence->getAllocationSize(); - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTzFormatString() - { - return 'Y-m-d H:i:s.uP'; - } - - /** - * {@inheritdoc} - */ - public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) - { - return 'TIMESTAMP WITH TIME ZONE'; - } - - /** - * {@inheritdoc} - */ - public function getDropSequenceSQL($sequence) - { - if ($sequence instanceof Sequence) { - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - - /** - * {@inheritdoc} - */ - public function getListSequencesSQL($database) - { - return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; - } - - /** - * {@inheritdoc} - */ - public function getSequenceNextValSQL($sequenceName) - { - return 'SELECT ' . $sequenceName . '.NEXTVAL'; - } - - /** - * {@inheritdoc} - */ - public function supportsSequences() - { - return true; - } - - /** - * {@inheritdoc} - */ - protected function getAdvancedIndexOptionsSQL(Index $index) - { - if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { - return ' WITH NULLS NOT DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); - } - - return parent::getAdvancedIndexOptionsSQL($index); - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLAnywhere12Keywords::class; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - parent::initializeDoctrineTypeMappings(); - $this->doctrineTypeMapping['timestamp with time zone'] = 'datetime'; - } -} diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php deleted file mode 100644 index 35d4238e4a3..00000000000 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywhere16Platform.php +++ /dev/null @@ -1,39 +0,0 @@ -hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { - throw new UnexpectedValueException( - 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' - ); - } - - if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { - return ' WITH NULLS DISTINCT' . parent::getAdvancedIndexOptionsSQL($index); - } - - return parent::getAdvancedIndexOptionsSQL($index); - } - - /** - * {@inheritdoc} - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLAnywhere16Keywords::class; - } -} diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php index b1105e90faa..d33477e8c51 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -10,10 +10,12 @@ use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Identifier; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; use Doctrine\DBAL\TransactionIsolationLevel; use InvalidArgumentException; +use UnexpectedValueException; use function array_merge; use function array_unique; use function array_values; @@ -32,7 +34,7 @@ /** * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the - * SAP Sybase SQL Anywhere 10 database platform. + * SAP Sybase SQL Anywhere 12 database platform. */ class SQLAnywherePlatform extends AbstractPlatform { @@ -508,7 +510,7 @@ public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) */ public function getDateTimeTzFormatString() { - return $this->getDateTimeFormatString(); + return 'Y-m-d H:i:s.uP'; } /** @@ -663,16 +665,6 @@ public function getForUpdateSQL() return ''; } - /** - * {@inheritdoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'NEWID()'; - } - /** * {@inheritdoc} */ @@ -996,6 +988,14 @@ public function getMd5Expression($column) return 'HASH(' . $column . ", 'MD5')"; } + /** + * {@inheritdoc} + */ + public function getRegexpExpression() + { + return 'REGEXP'; + } + /** * {@inheritdoc} */ @@ -1161,22 +1161,65 @@ public function getTruncateTableSQL($tableName, $cascade = false) /** * {@inheritdoc} */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) + public function getCreateSequenceSQL(Sequence $sequence) { - if ($index->isPrimary()) { - throw new InvalidArgumentException( - 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().' - ); - } + return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize() . + ' START WITH ' . $sequence->getInitialValue() . + ' MINVALUE ' . $sequence->getInitialValue(); + } - if (! $index->isUnique()) { - throw new InvalidArgumentException( - 'Can only create unique constraint declarations, no common index declarations with ' . - 'getUniqueConstraintDeclarationSQL().' - ); + /** + * {@inheritdoc} + */ + public function getAlterSequenceSQL(Sequence $sequence) + { + return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . + ' INCREMENT BY ' . $sequence->getAllocationSize(); + } + + /** + * {@inheritdoc} + */ + public function getDropSequenceSQL($sequence) + { + if ($sequence instanceof Sequence) { + $sequence = $sequence->getQuotedName($this); } - return $this->getTableConstraintDeclarationSQL($index, $name); + return 'DROP SEQUENCE ' . $sequence; + } + + /** + * {@inheritdoc} + */ + public function getListSequencesSQL($database) + { + return 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE'; + } + + /** + * {@inheritdoc} + */ + public function getSequenceNextValSQL($sequenceName) + { + return 'SELECT ' . $sequenceName . '.NEXTVAL'; + } + + /** + * {@inheritdoc} + */ + public function supportsSequences() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'TIMESTAMP WITH TIME ZONE'; } /** @@ -1309,16 +1352,16 @@ protected function _getTransactionIsolationLevelSQL($level) /** * {@inheritdoc} */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { $limitOffsetClause = ''; - if ($limit > 0) { + if ($limit !== null) { $limitOffsetClause = 'TOP ' . $limit . ' '; } if ($offset > 0) { - if ($limit === 0) { + if ($limit === null) { $limitOffsetClause = 'TOP ALL '; } @@ -1342,12 +1385,26 @@ protected function doModifyLimitQuery($query, $limit, $offset) */ protected function getAdvancedIndexOptionsSQL(Index $index) { + if ($index->hasFlag('with_nulls_distinct') && $index->hasFlag('with_nulls_not_distinct')) { + throw new UnexpectedValueException( + 'An Index can either have a "with_nulls_distinct" or "with_nulls_not_distinct" flag but not both.' + ); + } + $sql = ''; if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) { $sql .= ' FOR OLAP WORKLOAD'; } + if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_not_distinct')) { + return ' WITH NULLS NOT DISTINCT' . $sql; + } + + if (! $index->isPrimary() && $index->isUnique() && $index->hasFlag('with_nulls_distinct')) { + return ' WITH NULLS DISTINCT' . $sql; + } + return $sql; } @@ -1466,44 +1523,45 @@ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'char' => 'string', - 'long nvarchar' => 'text', - 'long varchar' => 'text', - 'nchar' => 'string', - 'ntext' => 'text', - 'nvarchar' => 'string', - 'text' => 'text', - 'uniqueidentifierstr' => 'guid', - 'varchar' => 'string', - 'xml' => 'text', - 'bigint' => 'bigint', - 'unsigned bigint' => 'bigint', - 'bit' => 'boolean', - 'decimal' => 'decimal', - 'double' => 'float', - 'float' => 'float', - 'int' => 'integer', - 'integer' => 'integer', - 'unsigned int' => 'integer', - 'numeric' => 'decimal', - 'smallint' => 'smallint', - 'unsigned smallint' => 'smallint', - 'tinyint' => 'smallint', - 'unsigned tinyint' => 'smallint', - 'money' => 'decimal', - 'smallmoney' => 'decimal', - 'long varbit' => 'text', - 'varbit' => 'string', - 'date' => 'date', - 'datetime' => 'datetime', - 'smalldatetime' => 'datetime', - 'time' => 'time', - 'timestamp' => 'datetime', - 'binary' => 'binary', - 'image' => 'blob', - 'long binary' => 'blob', - 'uniqueidentifier' => 'guid', - 'varbinary' => 'binary', + 'bigint' => 'bigint', + 'binary' => 'binary', + 'bit' => 'boolean', + 'char' => 'string', + 'decimal' => 'decimal', + 'date' => 'date', + 'datetime' => 'datetime', + 'double' => 'float', + 'float' => 'float', + 'image' => 'blob', + 'int' => 'integer', + 'integer' => 'integer', + 'long binary' => 'blob', + 'long nvarchar' => 'text', + 'long varbit' => 'text', + 'long varchar' => 'text', + 'money' => 'decimal', + 'nchar' => 'string', + 'ntext' => 'text', + 'numeric' => 'decimal', + 'nvarchar' => 'string', + 'smalldatetime' => 'datetime', + 'smallint' => 'smallint', + 'smallmoney' => 'decimal', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'timestamp with time zone' => 'datetime', + 'tinyint' => 'smallint', + 'uniqueidentifier' => 'guid', + 'uniqueidentifierstr' => 'guid', + 'unsigned bigint' => 'bigint', + 'unsigned int' => 'integer', + 'unsigned smallint' => 'smallint', + 'unsigned tinyint' => 'smallint', + 'varbinary' => 'binary', + 'varbit' => 'string', + 'varchar' => 'string', + 'xml' => 'text', ]; } } diff --git a/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php index a104848f84e..c95d93c9a86 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAzurePlatform.php @@ -11,7 +11,7 @@ * * - Create tables with the FEDERATED ON syntax. */ -class SQLAzurePlatform extends SQLServer2008Platform +class SQLAzurePlatform extends SQLServerPlatform { /** * {@inheritDoc} diff --git a/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php b/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php deleted file mode 100644 index 1026a934f00..00000000000 --- a/lib/Doctrine/DBAL/Platforms/SQLServer2005Platform.php +++ /dev/null @@ -1,46 +0,0 @@ -doctrineTypeMapping['datetime2'] = 'datetime'; - $this->doctrineTypeMapping['date'] = 'date'; - $this->doctrineTypeMapping['time'] = 'time'; - $this->doctrineTypeMapping['datetimeoffset'] = 'datetimetz'; - } - - /** - * {@inheritdoc} - * - * Returns Microsoft SQL Server 2008 specific keywords class - */ - protected function getReservedKeywordsClass() - { - return Keywords\SQLServer2008Keywords::class; - } - - protected function getLikeWildcardCharacters() : string - { - return parent::getLikeWildcardCharacters() . '[]^'; - } -} diff --git a/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php b/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php index 009a37d33b0..afc6a32b060 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServer2012Platform.php @@ -6,6 +6,7 @@ use const PREG_OFFSET_CAPTURE; use function preg_match; use function preg_match_all; +use function sprintf; use function substr_count; /** @@ -14,7 +15,7 @@ * Differences to SQL Server 2008 and before are that sequences are introduced, * and support for the new OFFSET... FETCH syntax for result pagination has been added. */ -class SQLServer2012Platform extends SQLServer2008Platform +class SQLServer2012Platform extends SQLServerPlatform { /** * {@inheritdoc} @@ -92,7 +93,7 @@ protected function getReservedKeywordsClass() /** * {@inheritdoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { if ($limit === null && $offset <= 0) { return $query; @@ -125,17 +126,13 @@ protected function doModifyLimitQuery($query, $limit, $offset = null) } } - if ($offset === null) { - $offset = 0; - } - // This looks somewhat like MYSQL, but limit/offset are in inverse positions // Supposedly SQL:2008 core standard. // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. - $query .= ' OFFSET ' . (int) $offset . ' ROWS'; + $query .= sprintf(' OFFSET %d ROWS', $offset); if ($limit !== null) { - $query .= ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY'; + $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); } return $query; diff --git a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php index 88578690d77..77751f0fd99 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -387,18 +387,6 @@ public function getDefaultConstraintDeclarationSQL($table, array $column) ' FOR ' . $columnName->getQuotedName($this); } - /** - * {@inheritDoc} - */ - public function getUniqueConstraintDeclarationSQL($name, Index $index) - { - $constraint = parent::getUniqueConstraintDeclarationSQL($name, $index); - - $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); - - return $constraint; - } - /** * {@inheritDoc} */ @@ -886,7 +874,7 @@ public function getListTablesSQL() { // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication - return "SELECT name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; + return "SELECT name, SCHEMA_NAME (uid) AS schema_name FROM sysobjects WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; } /** @@ -1014,16 +1002,6 @@ public function getDropViewSQL($name) return 'DROP VIEW ' . $name; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return 'NEWID()'; - } - /** * {@inheritDoc} */ @@ -1174,6 +1152,14 @@ public function getGuidTypeDeclarationSQL(array $field) return 'UNIQUEIDENTIFIER'; } + /** + * {@inheritDoc} + */ + public function getDateTimeTzTypeDeclarationSQL(array $fieldDeclaration) + { + return 'DATETIMEOFFSET(6)'; + } + /** * {@inheritDoc} */ @@ -1219,7 +1205,9 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef) */ public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) { - return 'DATETIME'; + // 3 - microseconds precision length + // http://msdn.microsoft.com/en-us/library/ms187819.aspx + return 'DATETIME2(6)'; } /** @@ -1227,7 +1215,7 @@ public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration) */ public function getDateTypeDeclarationSQL(array $fieldDeclaration) { - return 'DATETIME'; + return 'DATE'; } /** @@ -1235,7 +1223,7 @@ public function getDateTypeDeclarationSQL(array $fieldDeclaration) */ public function getTimeTypeDeclarationSQL(array $fieldDeclaration) { - return 'DATETIME'; + return 'TIME(0)'; } /** @@ -1249,7 +1237,7 @@ public function getBooleanTypeDeclarationSQL(array $field) /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset = null) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { $where = []; @@ -1381,7 +1369,7 @@ private function isOrderByInTopNSubquery($query, $currentPosition) */ public function supportsLimitOffset() { - return false; + return true; } /** @@ -1425,7 +1413,7 @@ public function getTemporaryTableName($tableName) */ public function getDateTimeFormatString() { - return 'Y-m-d H:i:s.000'; + return 'Y-m-d H:i:s.u'; } /** @@ -1433,7 +1421,7 @@ public function getDateTimeFormatString() */ public function getDateFormatString() { - return 'Y-m-d H:i:s.000'; + return 'Y-m-d'; } /** @@ -1441,7 +1429,7 @@ public function getDateFormatString() */ public function getTimeFormatString() { - return 'Y-m-d H:i:s.000'; + return 'H:i:s'; } /** @@ -1449,7 +1437,7 @@ public function getTimeFormatString() */ public function getDateTimeTzFormatString() { - return $this->getDateTimeFormatString(); + return 'Y-m-d H:i:s.u P'; } /** @@ -1466,31 +1454,35 @@ public function getName() protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'bigint' => 'bigint', - 'numeric' => 'decimal', - 'bit' => 'boolean', - 'smallint' => 'smallint', - 'decimal' => 'decimal', - 'smallmoney' => 'integer', - 'int' => 'integer', - 'tinyint' => 'smallint', - 'money' => 'integer', - 'float' => 'float', - 'real' => 'float', - 'double' => 'float', + 'bigint' => 'bigint', + 'binary' => 'binary', + 'bit' => 'boolean', + 'char' => 'string', + 'date' => 'date', + 'datetime' => 'datetime', + 'datetime2' => 'datetime', + 'datetimeoffset' => 'datetimetz', + 'decimal' => 'decimal', + 'double' => 'float', 'double precision' => 'float', - 'smalldatetime' => 'datetime', - 'datetime' => 'datetime', - 'char' => 'string', - 'varchar' => 'string', - 'text' => 'text', - 'nchar' => 'string', - 'nvarchar' => 'string', - 'ntext' => 'text', - 'binary' => 'binary', - 'varbinary' => 'binary', - 'image' => 'blob', + 'float' => 'float', + 'image' => 'blob', + 'int' => 'integer', + 'money' => 'integer', + 'nchar' => 'string', + 'ntext' => 'text', + 'numeric' => 'decimal', + 'nvarchar' => 'string', + 'real' => 'float', + 'smalldatetime' => 'datetime', + 'smallint' => 'smallint', + 'smallmoney' => 'integer', + 'text' => 'text', + 'time' => 'time', + 'tinyint' => 'smallint', 'uniqueidentifier' => 'guid', + 'varbinary' => 'binary', + 'varchar' => 'string', ]; } @@ -1651,6 +1643,14 @@ public function getColumnDeclarationSQL($name, array $field) return $name . ' ' . $columnDef; } + /** + * {@inheritdoc} + */ + protected function getLikeWildcardCharacters() : string + { + return parent::getLikeWildcardCharacters() . '[]^'; + } + /** * Returns a unique default constraint name for a table and column. * diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index 819421d3469..7ae69a515ad 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -40,19 +40,6 @@ public function getRegexpExpression() return 'REGEXP'; } - /** - * {@inheritDoc} - * - * @deprecated Use application-generated UUIDs instead - */ - public function getGuidExpression() - { - return "HEX(RANDOMBLOB(4)) || '-' || HEX(RANDOMBLOB(2)) || '-4' || " - . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || " - . "SUBSTR('89AB', 1 + (ABS(RANDOM()) % 4), 1) || " - . "SUBSTR(HEX(RANDOMBLOB(2)), 2) || '-' || HEX(RANDOMBLOB(6))"; - } - /** * {@inheritDoc} */ @@ -313,9 +300,9 @@ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) /** * {@inheritDoc} */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL($tableName, array $columns, array $options = []) { - $name = str_replace('.', '__', $name); + $tableName = str_replace('.', '__', $tableName); $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { @@ -332,7 +319,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] } } - $query = ['CREATE TABLE ' . $name . ' (' . $queryFields . ')']; + $query = ['CREATE TABLE ' . $tableName . ' (' . $queryFields . ')']; if (isset($options['alter']) && $options['alter'] === true) { return $query; @@ -340,13 +327,13 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $indexDef) { - $query[] = $this->getCreateIndexSQL($indexDef, $name); + $query[] = $this->getCreateIndexSQL($indexDef, $tableName); } } if (isset($options['unique']) && ! empty($options['unique'])) { foreach ($options['unique'] as $indexDef) { - $query[] = $this->getCreateIndexSQL($indexDef, $name); + $query[] = $this->getCreateIndexSQL($indexDef, $tableName); } } @@ -381,8 +368,9 @@ private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $ */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { - return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + return $fixed + ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); } /** @@ -611,38 +599,39 @@ public function getInlineColumnCommentSQL($comment) protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ - 'boolean' => 'boolean', - 'tinyint' => 'boolean', - 'smallint' => 'smallint', - 'mediumint' => 'integer', - 'int' => 'integer', - 'integer' => 'integer', - 'serial' => 'integer', 'bigint' => 'bigint', 'bigserial' => 'bigint', - 'clob' => 'text', - 'tinytext' => 'text', - 'mediumtext' => 'text', - 'longtext' => 'text', - 'text' => 'text', - 'varchar' => 'string', - 'longvarchar' => 'string', - 'varchar2' => 'string', - 'nvarchar' => 'string', - 'image' => 'string', - 'ntext' => 'string', + 'blob' => 'blob', + 'boolean' => 'boolean', 'char' => 'string', + 'clob' => 'text', 'date' => 'date', 'datetime' => 'datetime', - 'timestamp' => 'datetime', - 'time' => 'time', - 'float' => 'float', + 'decimal' => 'decimal', 'double' => 'float', 'double precision' => 'float', - 'real' => 'float', - 'decimal' => 'decimal', + 'float' => 'float', + 'image' => 'string', + 'int' => 'integer', + 'integer' => 'integer', + 'longtext' => 'text', + 'longvarchar' => 'string', + 'mediumint' => 'integer', + 'mediumtext' => 'text', + 'ntext' => 'string', 'numeric' => 'decimal', - 'blob' => 'blob', + 'nvarchar' => 'string', + 'real' => 'float', + 'serial' => 'integer', + 'smallint' => 'smallint', + 'string' => 'string', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'tinyint' => 'boolean', + 'tinytext' => 'text', + 'varchar' => 'string', + 'varchar2' => 'string', ]; } @@ -700,10 +689,10 @@ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) /** * {@inheritDoc} */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset) : string { if ($limit === null && $offset > 0) { - return $query . ' LIMIT -1 OFFSET ' . $offset; + $limit = -1; } return parent::doModifyLimitQuery($query, $limit, $offset); @@ -893,10 +882,11 @@ public function getAlterTableSQL(TableDiff $diff) $sql = []; $tableSql = []; + if (! $this->onSchemaAlterTable($diff, $tableSql)) { $dataTable = new Table('__temp__' . $table->getName()); - $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), $this->getForeignKeysInAlteredTable($diff), 0, $table->getOptions()); + $newTable = new Table($table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff), [], $this->getForeignKeysInAlteredTable($diff), $table->getOptions()); $newTable->addOption('alter', true); $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); diff --git a/lib/Doctrine/DBAL/Portability/Connection.php b/lib/Doctrine/DBAL/Portability/Connection.php index 32186dcb0ec..0fac1a9dffd 100644 --- a/lib/Doctrine/DBAL/Portability/Connection.php +++ b/lib/Doctrine/DBAL/Portability/Connection.php @@ -5,10 +5,11 @@ use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\ColumnCase; use Doctrine\DBAL\Driver\PDOConnection; +use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use PDO; use const CASE_LOWER; use const CASE_UPPER; -use function func_get_args; /** * Portability wrapper for a Connection. @@ -26,7 +27,6 @@ class Connection extends \Doctrine\DBAL\Connection public const PORTABILITY_POSTGRESQL = 13; public const PORTABILITY_SQLITE = 13; public const PORTABILITY_OTHERVENDORS = 12; - public const PORTABILITY_DRIZZLE = 13; public const PORTABILITY_SQLANYWHERE = 13; public const PORTABILITY_SQLSRV = 13; @@ -51,8 +51,6 @@ public function connect() $params['portability'] &= self::PORTABILITY_POSTGRESQL; } elseif ($this->getDatabasePlatform()->getName() === 'sqlite') { $params['portability'] &= self::PORTABILITY_SQLITE; - } elseif ($this->getDatabasePlatform()->getName() === 'drizzle') { - $params['portability'] &= self::PORTABILITY_DRIZZLE; } elseif ($this->getDatabasePlatform()->getName() === 'sqlanywhere') { $params['portability'] &= self::PORTABILITY_SQLANYWHERE; } elseif ($this->getDatabasePlatform()->getName() === 'db2') { @@ -68,7 +66,7 @@ public function connect() if (isset($params['fetch_case']) && $this->portability & self::PORTABILITY_FIX_CASE) { if ($this->_conn instanceof PDOConnection) { // make use of c-level support for case handling - $this->_conn->setAttribute(PDO::ATTR_CASE, $params['fetch_case']); + $this->_conn->getWrappedConnection()->setAttribute(PDO::ATTR_CASE, $params['fetch_case']); } else { $this->case = $params['fetch_case'] === ColumnCase::LOWER ? CASE_LOWER : CASE_UPPER; } @@ -97,7 +95,7 @@ public function getFetchCase() /** * {@inheritdoc} */ - public function executeQuery($query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) + public function executeQuery(string $query, array $params = [], $types = [], ?QueryCacheProfile $qcp = null) : ResultStatement { $stmt = new Statement(parent::executeQuery($query, $params, $types, $qcp), $this); $stmt->setFetchMode($this->defaultFetchMode); @@ -108,9 +106,9 @@ public function executeQuery($query, array $params = [], $types = [], ?QueryCach /** * {@inheritdoc} */ - public function prepare($statement) + public function prepare(string $sql) : DriverStatement { - $stmt = new Statement(parent::prepare($statement), $this); + $stmt = new Statement(parent::prepare($sql), $this); $stmt->setFetchMode($this->defaultFetchMode); return $stmt; @@ -119,11 +117,11 @@ public function prepare($statement) /** * {@inheritdoc} */ - public function query() + public function query(string $sql) : ResultStatement { $this->connect(); - $stmt = $this->_conn->query(...func_get_args()); + $stmt = $this->_conn->query($sql); $stmt = new Statement($stmt, $this); $stmt->setFetchMode($this->defaultFetchMode); diff --git a/lib/Doctrine/DBAL/Portability/Statement.php b/lib/Doctrine/DBAL/Portability/Statement.php index 1499ff1bf48..24bc166d3b0 100644 --- a/lib/Doctrine/DBAL/Portability/Statement.php +++ b/lib/Doctrine/DBAL/Portability/Statement.php @@ -7,7 +7,6 @@ use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use IteratorAggregate; -use PDO; use function array_change_key_case; use function is_string; use function rtrim; @@ -100,11 +99,11 @@ public function execute($params = null) /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg1 = null, $arg2 = null) + public function setFetchMode($fetchMode, ...$args) { $this->defaultFetchMode = $fetchMode; - return $this->stmt->setFetchMode($fetchMode, $arg1, $arg2); + return $this->stmt->setFetchMode($fetchMode, ...$args); } /** @@ -118,11 +117,11 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { $fetchMode = $fetchMode ?: $this->defaultFetchMode; - $row = $this->stmt->fetch($fetchMode); + $row = $this->stmt->fetch($fetchMode, ...$args); $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); $fixCase = $this->case !== null @@ -137,15 +136,11 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { $fetchMode = $fetchMode ?: $this->defaultFetchMode; - if ($fetchArgument) { - $rows = $this->stmt->fetchAll($fetchMode, $fetchArgument); - } else { - $rows = $this->stmt->fetchAll($fetchMode); - } + $rows = $this->stmt->fetchAll($fetchMode, ...$args); $iterateRow = $this->portability & (Connection::PORTABILITY_EMPTY_TO_NULL|Connection::PORTABILITY_RTRIM); $fixCase = $this->case !== null @@ -226,7 +221,7 @@ public function fetchColumn($columnIndex = 0) /** * {@inheritdoc} */ - public function rowCount() + public function rowCount() : int { return $this->stmt->rowCount(); } diff --git a/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php b/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php index 91f370aecb6..a017da1d822 100644 --- a/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php +++ b/lib/Doctrine/DBAL/Query/Expression/ExpressionBuilder.php @@ -286,13 +286,12 @@ public function notIn($x, $y) /** * Quotes a given input parameter. * - * @param mixed $input The parameter to be quoted. - * @param string|null $type The type of the parameter. + * @param string $input The parameter to be quoted. * * @return string */ - public function literal($input, $type = null) + public function literal($input) { - return $this->connection->quote($input, $type); + return $this->connection->quote($input); } } diff --git a/lib/Doctrine/DBAL/Query/QueryBuilder.php b/lib/Doctrine/DBAL/Query/QueryBuilder.php index 12584d960ab..57f6cd98959 100644 --- a/lib/Doctrine/DBAL/Query/QueryBuilder.php +++ b/lib/Doctrine/DBAL/Query/QueryBuilder.php @@ -1128,7 +1128,7 @@ private function getFromClauses() // Loop through all FROM clauses foreach ($this->sqlParts['from'] as $from) { - if ($from['alias'] === null) { + if ($from['alias'] === null || $from['alias'] === $from['table']) { $tableSql = $from['table']; $tableReference = $from['table']; } else { @@ -1187,7 +1187,14 @@ private function getSQLForInsert() */ private function getSQLForUpdate() { - $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $from = $this->sqlParts['from']; + + if ($from['alias'] === null || $from['alias'] === $from['table']) { + $table = $from['table']; + } else { + $table = $from['table'] . ' ' . $from['alias']; + } + return 'UPDATE ' . $table . ' SET ' . implode(', ', $this->sqlParts['set']) . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); @@ -1200,7 +1207,14 @@ private function getSQLForUpdate() */ private function getSQLForDelete() { - $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $from = $this->sqlParts['from']; + + if ($from['alias'] === null || $from['alias'] === $from['table']) { + $table = $from['table']; + } else { + $table = $from['table'] . ' ' . $from['alias']; + } + return 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); } diff --git a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php index 9b917427fd8..0d73b14f64f 100644 --- a/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/AbstractSchemaManager.php @@ -263,12 +263,14 @@ public function listTableDetails($tableName) { $columns = $this->listTableColumns($tableName); $foreignKeys = []; + if ($this->_platform->supportsForeignKeyConstraints()) { $foreignKeys = $this->listTableForeignKeys($tableName); } + $indexes = $this->listTableIndexes($tableName); - return new Table($tableName, $columns, $indexes, $foreignKeys, false, []); + return new Table($tableName, $columns, $indexes, [], $foreignKeys, []); } /** @@ -587,6 +589,7 @@ public function dropAndCreateView(View $view) public function alterTable(TableDiff $tableDiff) { $queries = $this->_platform->getAlterTableSQL($tableDiff); + if (! is_array($queries) || ! count($queries)) { return; } diff --git a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php index f5e225512ab..c962983bb8c 100644 --- a/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/DB2SchemaManager.php @@ -25,7 +25,15 @@ class DB2SchemaManager extends AbstractSchemaManager public function listTableNames() { $sql = $this->_platform->getListTablesSQL(); - $sql .= ' AND CREATOR = UPPER(' . $this->_conn->quote($this->_conn->getUsername()) . ')'; + $sql .= ' AND CREATOR = '; + + $user = $this->_conn->getUsername(); + + if ($user !== null) { + $sql .= 'UPPER(' . $this->_conn->quote($user) . ')'; + } else { + $sql .= 'CURRENT USER'; + } $tables = $this->_conn->fetchAll($sql); diff --git a/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php b/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php deleted file mode 100644 index c334db2797b..00000000000 --- a/lib/Doctrine/DBAL/Schema/DrizzleSchemaManager.php +++ /dev/null @@ -1,103 +0,0 @@ -_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); - $tableColumn['COLUMN_COMMENT'] = $this->removeDoctrineTypeFromComment($tableColumn['COLUMN_COMMENT'], $type); - - $options = [ - 'notnull' => ! (bool) $tableColumn['IS_NULLABLE'], - 'length' => (int) $tableColumn['CHARACTER_MAXIMUM_LENGTH'], - 'default' => $tableColumn['COLUMN_DEFAULT'] ?? null, - 'autoincrement' => (bool) $tableColumn['IS_AUTO_INCREMENT'], - 'scale' => (int) $tableColumn['NUMERIC_SCALE'], - 'precision' => (int) $tableColumn['NUMERIC_PRECISION'], - 'comment' => isset($tableColumn['COLUMN_COMMENT']) && $tableColumn['COLUMN_COMMENT'] !== '' - ? $tableColumn['COLUMN_COMMENT'] - : null, - ]; - - $column = new Column($tableColumn['COLUMN_NAME'], Type::getType($type), $options); - - if (! empty($tableColumn['COLLATION_NAME'])) { - $column->setPlatformOption('collation', $tableColumn['COLLATION_NAME']); - } - - return $column; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database['SCHEMA_NAME']; - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableDefinition($table) - { - return $table['TABLE_NAME']; - } - - /** - * {@inheritdoc} - */ - public function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - $columns = []; - foreach (explode(',', $tableForeignKey['CONSTRAINT_COLUMNS']) as $value) { - $columns[] = trim($value, ' `'); - } - - $refColumns = []; - foreach (explode(',', $tableForeignKey['REFERENCED_TABLE_COLUMNS']) as $value) { - $refColumns[] = trim($value, ' `'); - } - - return new ForeignKeyConstraint( - $columns, - $tableForeignKey['REFERENCED_TABLE_NAME'], - $refColumns, - $tableForeignKey['CONSTRAINT_NAME'], - [ - 'onUpdate' => $tableForeignKey['UPDATE_RULE'], - 'onDelete' => $tableForeignKey['DELETE_RULE'], - ] - ); - } - - /** - * {@inheritdoc} - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) - { - $indexes = []; - foreach ($tableIndexes as $k) { - $k['primary'] = (bool) $k['primary']; - $indexes[] = $k; - } - - return parent::_getPortableTableIndexesList($indexes, $tableName); - } -} diff --git a/lib/Doctrine/DBAL/Schema/Index.php b/lib/Doctrine/DBAL/Schema/Index.php index bae6d218cb6..ae658b69af2 100644 --- a/lib/Doctrine/DBAL/Schema/Index.php +++ b/lib/Doctrine/DBAL/Schema/Index.php @@ -57,6 +57,7 @@ public function __construct($indexName, array $columns, $isUnique = false, $isPr $isUnique = $isUnique || $isPrimary; $this->_setName($indexName); + $this->_isUnique = $isUnique; $this->_isPrimary = $isPrimary; $this->options = $options; @@ -64,6 +65,7 @@ public function __construct($indexName, array $columns, $isUnique = false, $isPr foreach ($columns as $column) { $this->_addColumn($column); } + foreach ($flags as $flag) { $this->addFlag($flag); } diff --git a/lib/Doctrine/DBAL/Schema/SchemaException.php b/lib/Doctrine/DBAL/Schema/SchemaException.php index 213d218475b..658663ac16a 100644 --- a/lib/Doctrine/DBAL/Schema/SchemaException.php +++ b/lib/Doctrine/DBAL/Schema/SchemaException.php @@ -18,7 +18,8 @@ class SchemaException extends DBALException public const SEQUENCE_ALREADY_EXISTS = 80; public const INDEX_INVALID_NAME = 90; public const FOREIGNKEY_DOESNT_EXIST = 100; - public const NAMESPACE_ALREADY_EXISTS = 110; + public const CONSTRAINT_DOESNT_EXIST = 110; + public const NAMESPACE_ALREADY_EXISTS = 120; /** * @param string $tableName @@ -142,6 +143,21 @@ public static function sequenceDoesNotExist($sequenceName) return new self("There exists no sequence with the name '" . $sequenceName . "'.", self::SEQUENCE_DOENST_EXIST); } + /** + * @param string $constraintName + * @param string $table + * + * @return self + */ + public static function uniqueConstraintDoesNotExist($constraintName, $table) + { + return new self(sprintf( + 'There exists no unique constraint with the name "%s" on table "%s".', + $constraintName, + $table + ), self::CONSTRAINT_DOESNT_EXIST); + } + /** * @param string $fkName * @param string $table diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 4822cf6b17d..e43c086cb82 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -5,15 +5,17 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Visitor\Visitor; use Doctrine\DBAL\Types\Type; -use const ARRAY_FILTER_USE_KEY; -use function array_filter; +use function array_keys; use function array_merge; +use function array_search; +use function array_unique; use function in_array; use function is_numeric; use function is_string; use function preg_match; use function strlen; use function strtolower; +use function uksort; /** * Object Representation of a table. @@ -35,6 +37,9 @@ class Table extends AbstractAsset /** @var string */ protected $_primaryKeyName = false; + /** @var UniqueConstraint[] */ + protected $_uniqueConstraints = []; + /** @var ForeignKeyConstraint[] */ protected $_fkConstraints = []; @@ -48,14 +53,20 @@ class Table extends AbstractAsset * @param string $tableName * @param Column[] $columns * @param Index[] $indexes + * @param UniqueConstraint[] $uniqueConstraints * @param ForeignKeyConstraint[] $fkConstraints - * @param int $idGeneratorType * @param mixed[] $options * * @throws DBALException */ - public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = []) - { + public function __construct( + $tableName, + array $columns = [], + array $indexes = [], + array $uniqueConstraints = [], + array $fkConstraints = [], + array $options = [] + ) { if (strlen($tableName) === 0) { throw DBALException::invalidTableName($tableName); } @@ -70,8 +81,12 @@ public function __construct($tableName, array $columns = [], array $indexes = [] $this->_addIndex($idx); } - foreach ($fkConstraints as $constraint) { - $this->_addForeignKeyConstraint($constraint); + foreach ($uniqueConstraints as $uniqueConstraint) { + $this->_addUniqueConstraint($uniqueConstraint); + } + + foreach ($fkConstraints as $fkConstraint) { + $this->_addForeignKeyConstraint($fkConstraint); } $this->_options = $options; @@ -85,18 +100,6 @@ public function setSchemaConfig(SchemaConfig $schemaConfig) $this->_schemaConfig = $schemaConfig; } - /** - * @return int - */ - protected function _getMaxIdentifierLength() - { - if ($this->_schemaConfig instanceof SchemaConfig) { - return $this->_schemaConfig->getMaxIdentifierLength(); - } - - return 63; - } - /** * Sets the Primary Key. * @@ -117,6 +120,27 @@ public function setPrimaryKey(array $columns, $indexName = false) return $this; } + /** + * @param mixed[] $columnNames + * @param string|null $indexName + * @param string[] $flags + * @param mixed[] $options + * + * @return self + */ + public function addUniqueConstraint(array $columnNames, $indexName = null, array $flags = [], array $options = []) + { + if ($indexName === null) { + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $columnNames), + 'uniq', + $this->_getMaxIdentifierLength() + ); + } + + return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); + } + /** * @param mixed[][] $columnNames * @param string|null $indexName @@ -161,9 +185,11 @@ public function dropPrimaryKey() public function dropIndex($indexName) { $indexName = $this->normalizeIdentifier($indexName); + if (! $this->hasIndex($indexName)) { throw SchemaException::indexDoesNotExist($indexName, $this->_name); } + unset($this->_indexes[$indexName]); } @@ -252,37 +278,6 @@ public function columnsAreIndexed(array $columnsNames) return false; } - /** - * @param mixed[][] $columnNames - * @param string $indexName - * @param bool $isUnique - * @param bool $isPrimary - * @param string[] $flags - * @param mixed[] $options - * - * @return Index - * - * @throws SchemaException - */ - private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = []) - { - if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { - throw SchemaException::indexNameInvalid($indexName); - } - - foreach ($columnNames as $columnName => $indexColOptions) { - if (is_numeric($columnName) && is_string($indexColOptions)) { - $columnName = $indexColOptions; - } - - if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); - } - } - - return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); - } - /** * @param string $columnName * @param string $typeName @@ -311,9 +306,11 @@ public function addColumn($columnName, $typeName, array $options = []) */ public function renameColumn($oldColumnName, $newColumnName) { - throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' . - 'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' . - 'column was renamed or one column was created and another one dropped.'); + throw new DBALException( + 'Table#renameColumn() was removed, because it drops and recreates the column instead. ' . + 'There is no fix available, because a schema diff cannot reliably detect if a column ' . + 'was renamed or one column was created and another one dropped.' + ); } /** @@ -327,6 +324,7 @@ public function renameColumn($oldColumnName, $newColumnName) public function changeColumn($columnName, array $options) { $column = $this->getColumn($columnName); + $column->setOptions($options); return $this; @@ -342,6 +340,7 @@ public function changeColumn($columnName, array $options) public function dropColumn($columnName) { $columnName = $this->normalizeIdentifier($columnName); + unset($this->_columns[$columnName]); return $this; @@ -362,7 +361,13 @@ public function dropColumn($columnName) */ public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null) { - $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength()); + if (! $constraintName) { + $constraintName = $this->_generateIdentifierName( + array_merge((array) $this->getName(), $localColumnNames), + 'fk', + $this->_getMaxIdentifierLength() + ); + } return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options); } @@ -424,9 +429,8 @@ public function addNamedForeignKeyConstraint($name, $foreignTable, array $localC $name, $options ); - $this->_addForeignKeyConstraint($constraint); - return $this; + return $this->_addForeignKeyConstraint($constraint); } /** @@ -443,137 +447,95 @@ public function addOption($name, $value) } /** - * @return void + * Returns whether this table has a foreign key constraint with the given name. * - * @throws SchemaException + * @param string $constraintName + * + * @return bool */ - protected function _addColumn(Column $column) + public function hasForeignKey($constraintName) { - $columnName = $column->getName(); - $columnName = $this->normalizeIdentifier($columnName); - - if (isset($this->_columns[$columnName])) { - throw SchemaException::columnAlreadyExists($this->getName(), $columnName); - } + $constraintName = $this->normalizeIdentifier($constraintName); - $this->_columns[$columnName] = $column; + return isset($this->_fkConstraints[$constraintName]); } /** - * Adds an index to the table. + * Returns the foreign key constraint with the given name. * - * @return self + * @param string $constraintName The constraint name. * - * @throws SchemaException + * @return ForeignKeyConstraint + * + * @throws SchemaException If the foreign key does not exist. */ - protected function _addIndex(Index $indexCandidate) + public function getForeignKey($constraintName) { - $indexName = $indexCandidate->getName(); - $indexName = $this->normalizeIdentifier($indexName); - $replacedImplicitIndexes = []; - - foreach ($this->implicitIndexes as $name => $implicitIndex) { - if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { - continue; - } - - $replacedImplicitIndexes[] = $name; - } - - if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || - ($this->_primaryKeyName !== false && $indexCandidate->isPrimary()) - ) { - throw SchemaException::indexAlreadyExists($indexName, $this->_name); - } - - foreach ($replacedImplicitIndexes as $name) { - unset($this->_indexes[$name], $this->implicitIndexes[$name]); - } + $constraintName = $this->normalizeIdentifier($constraintName); - if ($indexCandidate->isPrimary()) { - $this->_primaryKeyName = $indexName; + if (! $this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); } - $this->_indexes[$indexName] = $indexCandidate; - - return $this; + return $this->_fkConstraints[$constraintName]; } /** + * Removes the foreign key constraint with the given name. + * + * @param string $constraintName The constraint name. + * * @return void + * + * @throws SchemaException */ - protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + public function removeForeignKey($constraintName) { - $constraint->setLocalTable($this); - - if (strlen($constraint->getName())) { - $name = $constraint->getName(); - } else { - $name = $this->_generateIdentifierName( - array_merge((array) $this->getName(), $constraint->getLocalColumns()), - 'fk', - $this->_getMaxIdentifierLength() - ); - } - $name = $this->normalizeIdentifier($name); - - $this->_fkConstraints[$name] = $constraint; - - // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request. - // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes - // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns). - $indexName = $this->_generateIdentifierName( - array_merge([$this->getName()], $constraint->getColumns()), - 'idx', - $this->_getMaxIdentifierLength() - ); - $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + $constraintName = $this->normalizeIdentifier($constraintName); - foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFullfilledBy($existingIndex)) { - return; - } + if (! $this->hasForeignKey($constraintName)) { + throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); } - $this->_addIndex($indexCandidate); - $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + unset($this->_fkConstraints[$constraintName]); } /** - * Returns whether this table has a foreign key constraint with the given name. + * Returns whether this table has a unique constraint with the given name. * * @param string $constraintName * * @return bool */ - public function hasForeignKey($constraintName) + public function hasUniqueConstraint($constraintName) { $constraintName = $this->normalizeIdentifier($constraintName); - return isset($this->_fkConstraints[$constraintName]); + return isset($this->_uniqueConstraints[$constraintName]); } /** - * Returns the foreign key constraint with the given name. + * Returns the unique constraint with the given name. * * @param string $constraintName The constraint name. * - * @return ForeignKeyConstraint + * @return UniqueConstraint * * @throws SchemaException If the foreign key does not exist. */ - public function getForeignKey($constraintName) + public function getUniqueConstraint($constraintName) { $constraintName = $this->normalizeIdentifier($constraintName); - if (! $this->hasForeignKey($constraintName)) { - throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + + if (! $this->hasUniqueConstraint($constraintName)) { + throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name); } - return $this->_fkConstraints[$constraintName]; + return $this->_uniqueConstraints[$constraintName]; } /** - * Removes the foreign key constraint with the given name. + * Removes the unique constraint with the given name. * * @param string $constraintName The constraint name. * @@ -581,14 +543,15 @@ public function getForeignKey($constraintName) * * @throws SchemaException */ - public function removeForeignKey($constraintName) + public function removeUniqueConstraint($constraintName) { $constraintName = $this->normalizeIdentifier($constraintName); - if (! $this->hasForeignKey($constraintName)) { - throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name); + + if (! $this->hasUniqueConstraint($constraintName)) { + throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name); } - unset($this->_fkConstraints[$constraintName]); + unset($this->_uniqueConstraints[$constraintName]); } /** @@ -598,41 +561,26 @@ public function removeForeignKey($constraintName) */ public function getColumns() { - $primaryKeyColumns = []; + $columns = $this->_columns; + $pkCols = []; + $fkCols = []; + if ($this->hasPrimaryKey()) { - $primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns()); + $pkCols = $this->getPrimaryKey()->getColumns(); } - return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns); - } - - /** - * Returns foreign key columns - * - * @return Column[] - */ - private function getForeignKeyColumns() - { - $foreignKeyColumns = []; - foreach ($this->getForeignKeys() as $foreignKey) { - /** @var ForeignKeyConstraint $foreignKey */ - $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns()); + foreach ($this->getForeignKeys() as $fk) { + /** @var ForeignKeyConstraint $fk */ + $fkCols = array_merge($fkCols, $fk->getColumns()); } - return $this->filterColumns($foreignKeyColumns); - } - /** - * Returns only columns that have specified names - * - * @param string[] $columnNames - * - * @return Column[] - */ - private function filterColumns(array $columnNames) - { - return array_filter($this->_columns, static function ($columnName) use ($columnNames) { - return in_array($columnName, $columnNames, true); - }, ARRAY_FILTER_USE_KEY); + $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns))); + + uksort($columns, static function ($a, $b) use ($colNames) { + return array_search($a, $colNames) >= array_search($b, $colNames); + }); + + return $columns; } /** @@ -661,6 +609,7 @@ public function hasColumn($columnName) public function getColumn($columnName) { $columnName = $this->normalizeIdentifier($columnName); + if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } @@ -675,11 +624,9 @@ public function getColumn($columnName) */ public function getPrimaryKey() { - if (! $this->hasPrimaryKey()) { - return null; - } - - return $this->getIndex($this->_primaryKeyName); + return $this->hasPrimaryKey() + ? $this->getIndex($this->_primaryKeyName) + : null; } /** @@ -733,6 +680,7 @@ public function hasIndex($indexName) public function getIndex($indexName) { $indexName = $this->normalizeIdentifier($indexName); + if (! $this->hasIndex($indexName)) { throw SchemaException::indexDoesNotExist($indexName, $this->_name); } @@ -748,6 +696,16 @@ public function getIndexes() return $this->_indexes; } + /** + * Returns the unique constraints. + * + * @return UniqueConstraint[] + */ + public function getUniqueConstraints() + { + return $this->_uniqueConstraints; + } + /** * Returns the foreign key constraints. * @@ -816,15 +774,166 @@ public function __clone() foreach ($this->_columns as $k => $column) { $this->_columns[$k] = clone $column; } + foreach ($this->_indexes as $k => $index) { $this->_indexes[$k] = clone $index; } + foreach ($this->_fkConstraints as $k => $fk) { $this->_fkConstraints[$k] = clone $fk; $this->_fkConstraints[$k]->setLocalTable($this); } } + /** + * @return int + */ + protected function _getMaxIdentifierLength() + { + return $this->_schemaConfig instanceof SchemaConfig + ? $this->_schemaConfig->getMaxIdentifierLength() + : 63; + } + + /** + * @return void + * + * @throws SchemaException + */ + protected function _addColumn(Column $column) + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw SchemaException::columnAlreadyExists($this->getName(), $columnName); + } + + $this->_columns[$columnName] = $column; + } + + /** + * Adds an index to the table. + * + * @return self + * + * @throws SchemaException + */ + protected function _addIndex(Index $indexCandidate) + { + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = []; + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { + continue; + } + + $replacedImplicitIndexes[] = $name; + } + + if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName !== false && $indexCandidate->isPrimary()) + ) { + throw SchemaException::indexAlreadyExists($indexName, $this->_name); + } + + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; + } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; + } + + /** + * @return self + */ + protected function _addUniqueConstraint(UniqueConstraint $constraint) + { + $name = strlen($constraint->getName()) + ? $constraint->getName() + : $this->_generateIdentifierName( + array_merge((array) $this->getName(), $constraint->getLocalColumns()), + 'fk', + $this->_getMaxIdentifierLength() + ); + + $name = $this->normalizeIdentifier($name); + + $this->_uniqueConstraints[$name] = $constraint; + + // If there is already an index that fulfills this requirements drop the request. In the case of __construct + // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. + // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $constraint->getColumns()), + 'idx', + $this->_getMaxIdentifierLength() + ); + + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; + } + + /** + * @return self + */ + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) + { + $constraint->setLocalTable($this); + + $name = strlen($constraint->getName()) + ? $constraint->getName() + : $this->_generateIdentifierName( + array_merge((array) $this->getName(), $constraint->getLocalColumns()), + 'fk', + $this->_getMaxIdentifierLength() + ); + + $name = $this->normalizeIdentifier($name); + + $this->_fkConstraints[$name] = $constraint; + + // add an explicit index on the foreign key columns. + // If there is already an index that fulfills this requirements drop the request. In the case of __construct + // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. + // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). + $indexName = $this->_generateIdentifierName( + array_merge([$this->getName()], $constraint->getColumns()), + 'idx', + $this->_getMaxIdentifierLength() + ); + + $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFullfilledBy($existingIndex)) { + return $this; + } + } + + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; + } + /** * Normalizes a given identifier. * @@ -838,4 +947,64 @@ private function normalizeIdentifier($identifier) { return $this->trimQuotes(strtolower($identifier)); } + + /** + * @param mixed[] $columnNames + * @param string $indexName + * @param mixed[] $flags + * @param mixed[] $options + * + * @return UniqueConstraint + * + * @throws SchemaException + */ + private function _createUniqueConstraint(array $columnNames, $indexName, array $flags = [], array $options = []) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if (! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new UniqueConstraint($indexName, $columnNames, $flags, $options); + } + + /** + * @param mixed[] $columnNames + * @param string $indexName + * @param bool $isUnique + * @param bool $isPrimary + * @param string[] $flags + * @param mixed[] $options + * + * @return Index + * + * @throws SchemaException + */ + private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = []) + { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) { + throw SchemaException::indexNameInvalid($indexName); + } + + foreach ($columnNames as $columnName => $indexColOptions) { + if (is_numeric($columnName) && is_string($indexColOptions)) { + $columnName = $indexColOptions; + } + + if (! $this->hasColumn($columnName)) { + throw SchemaException::columnDoesNotExist($columnName, $this->_name); + } + } + + return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); + } } diff --git a/lib/Doctrine/DBAL/Schema/UniqueConstraint.php b/lib/Doctrine/DBAL/Schema/UniqueConstraint.php new file mode 100644 index 00000000000..8d47424769c --- /dev/null +++ b/lib/Doctrine/DBAL/Schema/UniqueConstraint.php @@ -0,0 +1,184 @@ + Identifier) + * + * @var Identifier[] + */ + protected $columns = []; + + /** + * Platform specific flags + * array($flagName => true) + * + * @var true[] + */ + protected $flags = []; + + /** + * Platform specific options + * + * @var mixed[] + */ + private $options = []; + + /** + * @param string $indexName + * @param string[] $columns + * @param string[] $flags + * @param mixed[] $options + */ + public function __construct($indexName, array $columns, array $flags = [], array $options = []) + { + $this->_setName($indexName); + + $this->options = $options; + + foreach ($columns as $column) { + $this->_addColumn($column); + } + + foreach ($flags as $flag) { + $this->addFlag($flag); + } + } + + /** + * {@inheritdoc} + */ + public function getColumns() + { + return array_keys($this->columns); + } + + /** + * {@inheritdoc} + */ + public function getQuotedColumns(AbstractPlatform $platform) + { + $columns = []; + + foreach ($this->columns as $column) { + $columns[] = $column->getQuotedName($platform); + } + + return $columns; + } + + /** + * @return string[] + */ + public function getUnquotedColumns() + { + return array_map([$this, 'trimQuotes'], $this->getColumns()); + } + + /** + * Returns platform specific flags for unique constraint. + * + * @return string[] + */ + public function getFlags() + { + return array_keys($this->flags); + } + + /** + * Adds flag for a unique constraint that translates to platform specific handling. + * + * @param string $flag + * + * @return self + * + * @example $uniqueConstraint->addFlag('CLUSTERED') + */ + public function addFlag($flag) + { + $this->flags[strtolower($flag)] = true; + + return $this; + } + + /** + * Does this unique constraint have a specific flag? + * + * @param string $flag + * + * @return bool + */ + public function hasFlag($flag) + { + return isset($this->flags[strtolower($flag)]); + } + + /** + * Removes a flag. + * + * @param string $flag + * + * @return void + */ + public function removeFlag($flag) + { + unset($this->flags[strtolower($flag)]); + } + + /** + * @param string $name + * + * @return bool + */ + public function hasOption($name) + { + return isset($this->options[strtolower($name)]); + } + + /** + * @param string $name + * + * @return mixed + */ + public function getOption($name) + { + return $this->options[strtolower($name)]; + } + + /** + * @return mixed[] + */ + public function getOptions() + { + return $this->options; + } + + /** + * @param string $column + * + * @return void + * + * @throws InvalidArgumentException + */ + protected function _addColumn($column) + { + if (! is_string($column)) { + throw new InvalidArgumentException('Expecting a string as Index Column'); + } + + $this->columns[$column] = new Identifier($column); + } +} diff --git a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php index 7c5dfe5c4be..ac17027c6b1 100644 --- a/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php +++ b/lib/Doctrine/DBAL/Sharding/SQLAzure/SQLAzureShardManager.php @@ -5,7 +5,6 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Sharding\ShardingException; use Doctrine\DBAL\Sharding\ShardManager; -use Doctrine\DBAL\Types\Type; use RuntimeException; use function is_bool; use function is_scalar; @@ -198,17 +197,15 @@ public function queryAll($sql, array $params = [], array $types = []) /** * Splits Federation at a given distribution value. * - * @param mixed $splitDistributionValue + * @param string $splitDistributionValue * * @return void */ public function splitFederation($splitDistributionValue) { - $type = Type::getType($this->distributionType); - $sql = 'ALTER FEDERATION ' . $this->getFederationName() . ' ' . 'SPLIT AT (' . $this->getDistributionKey() . ' = ' . - $this->conn->quote($splitDistributionValue, $type->getBindingType()) . ')'; + $this->conn->quote($splitDistributionValue) . ')'; $this->conn->exec($sql); } } diff --git a/lib/Doctrine/DBAL/Statement.php b/lib/Doctrine/DBAL/Statement.php index b65fa95e33a..fd8db886b8b 100644 --- a/lib/Doctrine/DBAL/Statement.php +++ b/lib/Doctrine/DBAL/Statement.php @@ -6,7 +6,6 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use IteratorAggregate; -use PDO; use Throwable; use function is_array; use function is_string; @@ -145,16 +144,12 @@ public function execute($params = null) } $logger = $this->conn->getConfiguration()->getSQLLogger(); - if ($logger) { - $logger->startQuery($this->sql, $this->params, $this->types); - } + $logger->startQuery($this->sql, $this->params, $this->types); try { $stmt = $this->stmt->execute($params); } catch (Throwable $ex) { - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); throw DBALException::driverExceptionDuringQuery( $this->conn->getDriver(), $ex, @@ -163,9 +158,7 @@ public function execute($params = null) ); } - if ($logger) { - $logger->stopQuery(); - } + $logger->stopQuery(); $this->params = []; $this->types = []; @@ -213,15 +206,9 @@ public function errorInfo() /** * {@inheritdoc} */ - public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null) + public function setFetchMode($fetchMode, ...$args) { - if ($arg2 === null) { - return $this->stmt->setFetchMode($fetchMode); - } elseif ($arg3 === null) { - return $this->stmt->setFetchMode($fetchMode, $arg2); - } - - return $this->stmt->setFetchMode($fetchMode, $arg2, $arg3); + return $this->stmt->setFetchMode($fetchMode, ...$args); } /** @@ -237,21 +224,17 @@ public function getIterator() /** * {@inheritdoc} */ - public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + public function fetch($fetchMode = null, ...$args) { - return $this->stmt->fetch($fetchMode); + return $this->stmt->fetch($fetchMode, ...$args); } /** * {@inheritdoc} */ - public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) + public function fetchAll($fetchMode = null, ...$args) { - if ($fetchArgument) { - return $this->stmt->fetchAll($fetchMode, $fetchArgument); - } - - return $this->stmt->fetchAll($fetchMode); + return $this->stmt->fetchAll($fetchMode, ...$args); } /** @@ -267,7 +250,7 @@ public function fetchColumn($columnIndex = 0) * * @return int The number of affected rows. */ - public function rowCount() + public function rowCount() : int { return $this->stmt->rowCount(); } diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php deleted file mode 100644 index 0e815663786..00000000000 --- a/lib/Doctrine/DBAL/Tools/Console/Command/ImportCommand.php +++ /dev/null @@ -1,129 +0,0 @@ -setName('dbal:import') - ->setDescription('Import SQL file(s) directly to Database.') - ->setDefinition([new InputArgument( - 'file', - InputArgument::REQUIRED | InputArgument::IS_ARRAY, - 'File path(s) of SQL to be executed.' - ), - ]) - ->setHelp(<<getHelper('db')->getConnection(); - - $fileNames = $input->getArgument('file'); - - if ($fileNames === null) { - return null; - } - - foreach ((array) $fileNames as $fileName) { - $filePath = realpath($fileName); - - // Phar compatibility. - if ($filePath === false) { - $filePath = $fileName; - } - - if (! file_exists($filePath)) { - throw new InvalidArgumentException( - sprintf("SQL file '%s' does not exist.", $filePath) - ); - } elseif (! is_readable($filePath)) { - throw new InvalidArgumentException( - sprintf("SQL file '%s' does not have read permissions.", $filePath) - ); - } - - $output->write(sprintf("Processing file '%s'... ", $filePath)); - $sql = file_get_contents($filePath); - - if ($conn instanceof PDOConnection) { - // PDO Drivers - try { - $lines = 0; - - $stmt = $conn->prepare($sql); - assert($stmt instanceof PDOStatement); - - $stmt->execute(); - - do { - // Required due to "MySQL has gone away!" issue - $stmt->fetch(); - $stmt->closeCursor(); - - $lines++; - } while ($stmt->nextRowset()); - - $output->write(sprintf('%d statements executed!', $lines) . PHP_EOL); - } catch (PDOException $e) { - $output->write('error!' . PHP_EOL); - - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } else { - // Non-PDO Drivers (ie. OCI8 driver) - $stmt = $conn->prepare($sql); - $rs = $stmt->execute(); - - if (! $rs) { - $error = $stmt->errorInfo(); - - $output->write('error!' . PHP_EOL); - - throw new RuntimeException($error[2], $error[0]); - } - - $output->writeln('OK!' . PHP_EOL); - - $stmt->closeCursor(); - } - } - - return null; - } -} diff --git a/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php b/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php index fb8b6b5c46f..238aa7d74f8 100644 --- a/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php +++ b/lib/Doctrine/DBAL/Tools/Console/Command/ReservedWordsCommand.php @@ -8,17 +8,12 @@ use Doctrine\DBAL\Platforms\Keywords\MySQL80Keywords; use Doctrine\DBAL\Platforms\Keywords\MySQLKeywords; use Doctrine\DBAL\Platforms\Keywords\OracleKeywords; -use Doctrine\DBAL\Platforms\Keywords\PostgreSQL91Keywords; -use Doctrine\DBAL\Platforms\Keywords\PostgreSQL92Keywords; +use Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords; +use Doctrine\DBAL\Platforms\Keywords\PostgreSQL94Keywords; use Doctrine\DBAL\Platforms\Keywords\PostgreSQLKeywords; use Doctrine\DBAL\Platforms\Keywords\ReservedKeywordsValidator; -use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere11Keywords; -use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere12Keywords; -use Doctrine\DBAL\Platforms\Keywords\SQLAnywhere16Keywords; use Doctrine\DBAL\Platforms\Keywords\SQLAnywhereKeywords; use Doctrine\DBAL\Platforms\Keywords\SQLiteKeywords; -use Doctrine\DBAL\Platforms\Keywords\SQLServer2005Keywords; -use Doctrine\DBAL\Platforms\Keywords\SQLServer2008Keywords; use Doctrine\DBAL\Platforms\Keywords\SQLServer2012Keywords; use Doctrine\DBAL\Platforms\Keywords\SQLServerKeywords; use Doctrine\DBAL\Schema\Schema; @@ -35,23 +30,18 @@ class ReservedWordsCommand extends Command { /** @var string[] */ private $keywordListClasses = [ + 'db2' => DB2Keywords::class, 'mysql' => MySQLKeywords::class, 'mysql57' => MySQL57Keywords::class, 'mysql80' => MySQL80Keywords::class, - 'sqlserver' => SQLServerKeywords::class, - 'sqlserver2005' => SQLServer2005Keywords::class, - 'sqlserver2008' => SQLServer2008Keywords::class, - 'sqlserver2012' => SQLServer2012Keywords::class, - 'sqlite' => SQLiteKeywords::class, - 'pgsql' => PostgreSQLKeywords::class, - 'pgsql91' => PostgreSQL91Keywords::class, - 'pgsql92' => PostgreSQL92Keywords::class, 'oracle' => OracleKeywords::class, - 'db2' => DB2Keywords::class, + 'pgsql' => PostgreSQLKeywords::class, + 'pgsql94' => PostgreSQL94Keywords::class, + 'pgsql100' => PostgreSQL100Keywords::class, 'sqlanywhere' => SQLAnywhereKeywords::class, - 'sqlanywhere11' => SQLAnywhere11Keywords::class, - 'sqlanywhere12' => SQLAnywhere12Keywords::class, - 'sqlanywhere16' => SQLAnywhere16Keywords::class, + 'sqlite' => SQLiteKeywords::class, + 'sqlserver' => SQLServerKeywords::class, + 'sqlserver2012' => SQLServer2012Keywords::class, ]; /** @@ -102,17 +92,13 @@ protected function configure() * mysql57 * mysql80 * pgsql - * pgsql92 + * pgsql94 + * pgsql100 * sqlite * oracle * sqlserver - * sqlserver2005 - * sqlserver2008 * sqlserver2012 * sqlanywhere - * sqlanywhere11 - * sqlanywhere12 - * sqlanywhere16 * db2 (Not checked by default) EOT ); @@ -128,23 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $keywordLists = (array) $input->getOption('list'); if (! $keywordLists) { - $keywordLists = [ - 'mysql', - 'mysql57', - 'mysql80', - 'pgsql', - 'pgsql92', - 'sqlite', - 'oracle', - 'sqlserver', - 'sqlserver2005', - 'sqlserver2008', - 'sqlserver2012', - 'sqlanywhere', - 'sqlanywhere11', - 'sqlanywhere12', - 'sqlanywhere16', - ]; + $keywordLists = array_keys($this->keywordListClasses); } $keywords = []; diff --git a/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php b/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php index 520a9af80aa..0df6f086f2c 100644 --- a/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php +++ b/lib/Doctrine/DBAL/Tools/Console/ConsoleRunner.php @@ -3,11 +3,10 @@ namespace Doctrine\DBAL\Tools\Console; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Tools\Console\Command\ImportCommand; use Doctrine\DBAL\Tools\Console\Command\ReservedWordsCommand; use Doctrine\DBAL\Tools\Console\Command\RunSqlCommand; use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper; -use Doctrine\DBAL\Version; +use PackageVersions\Versions; use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\HelperSet; @@ -38,7 +37,7 @@ public static function createHelperSet(Connection $connection) */ public static function run(HelperSet $helperSet, $commands = []) { - $cli = new Application('Doctrine Command Line Interface', Version::VERSION); + $cli = new Application('Doctrine Command Line Interface', Versions::getVersion('doctrine/dbal')); $cli->setCatchExceptions(true); $cli->setHelperSet($helperSet); @@ -56,7 +55,6 @@ public static function addCommands(Application $cli) { $cli->addCommands([ new RunSqlCommand(), - new ImportCommand(), new ReservedWordsCommand(), ]); } diff --git a/lib/Doctrine/DBAL/Types/BinaryType.php b/lib/Doctrine/DBAL/Types/BinaryType.php index 14362e840ae..e40f0010493 100644 --- a/lib/Doctrine/DBAL/Types/BinaryType.php +++ b/lib/Doctrine/DBAL/Types/BinaryType.php @@ -4,11 +4,9 @@ use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; -use function fopen; -use function fseek; -use function fwrite; use function is_resource; use function is_string; +use function stream_get_contents; /** * Type that maps ab SQL BINARY/VARBINARY to a PHP resource stream. @@ -32,14 +30,11 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return null; } - if (is_string($value)) { - $fp = fopen('php://temp', 'rb+'); - fwrite($fp, $value); - fseek($fp, 0); - $value = $fp; + if (is_resource($value)) { + $value = stream_get_contents($value); } - if (! is_resource($value)) { + if (! is_string($value)) { throw ConversionException::conversionFailed($value, self::BINARY); } diff --git a/lib/Doctrine/DBAL/Types/StringType.php b/lib/Doctrine/DBAL/Types/StringType.php index 879359a13d8..c6197df949a 100644 --- a/lib/Doctrine/DBAL/Types/StringType.php +++ b/lib/Doctrine/DBAL/Types/StringType.php @@ -17,14 +17,6 @@ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $pla return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); } - /** - * {@inheritdoc} - */ - public function getDefaultLength(AbstractPlatform $platform) - { - return $platform->getVarcharDefaultLength(); - } - /** * {@inheritdoc} */ diff --git a/lib/Doctrine/DBAL/Types/Type.php b/lib/Doctrine/DBAL/Types/Type.php index ec1ab681356..6cee27cedab 100644 --- a/lib/Doctrine/DBAL/Types/Type.php +++ b/lib/Doctrine/DBAL/Types/Type.php @@ -5,9 +5,6 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; -use function end; -use function explode; -use function str_replace; /** * The base class for so-called Doctrine mapping types. @@ -117,18 +114,6 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $value; } - /** - * Gets the default length of this type. - * - * @deprecated Rely on information provided by the platform instead. - * - * @return int|null - */ - public function getDefaultLength(AbstractPlatform $platform) - { - return null; - } - /** * Gets the SQL declaration snippet for a field of this type. * @@ -248,18 +233,6 @@ public static function getTypesMap() return self::$_typesMap; } - /** - * @deprecated Relying on string representation is discouraged and will be removed in DBAL 3.0. - * - * @return string - */ - public function __toString() - { - $e = explode('\\', static::class); - - return str_replace('Type', '', end($e)); - } - /** * Does working with this column require SQL conversion functions? * diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php deleted file mode 100644 index 60530e69a76..00000000000 --- a/lib/Doctrine/DBAL/Version.php +++ /dev/null @@ -1,33 +0,0 @@ -driver->getName()); - } - - public function testThrowsExceptionOnCreatingDatabasePlatformsForInvalidVersion() - { - $this->markTestSkipped('This test does not work on Drizzle as it is not version aware.'); - } - - protected function createDriver() - { - return new Driver(); - } - - protected function createPlatform() - { - return new DrizzlePlatform(); - } - - protected function createSchemaManager(Connection $connection) - { - return new DrizzleSchemaManager($connection); - } - - /** - * @return mixed[][] - */ - protected function getDatabasePlatformsForVersions() : array - { - return [ - ['foo', DrizzlePlatform::class], - ['bar', DrizzlePlatform::class], - ['baz', DrizzlePlatform::class], - ]; - } -} diff --git a/tests/Doctrine/Tests/DBAL/Driver/Mysqli/MysqliConnectionTest.php b/tests/Doctrine/Tests/DBAL/Driver/Mysqli/MysqliConnectionTest.php index 9bd3172dd1d..ba75f3d361b 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/Mysqli/MysqliConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/Mysqli/MysqliConnectionTest.php @@ -53,7 +53,7 @@ public function testRestoresErrorHandlerOnException() new MysqliConnection(['host' => '255.255.255.255'], 'user', 'pass'); self::fail('An exception was supposed to be raised'); } catch (MysqliException $e) { - self::assertSame('Network is unreachable', $e->getMessage()); + // Do nothing } self::assertSame($handler, set_error_handler($default_handler), 'Restoring error handler failed.'); diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOIbm/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOIbm/DriverTest.php deleted file mode 100644 index 6361f05575c..00000000000 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOIbm/DriverTest.php +++ /dev/null @@ -1,19 +0,0 @@ -driver->getName()); - } - - protected function createDriver() - { - return new Driver(); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php b/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php index a6ac2456b64..1e3a2745b65 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/PDOPgSql/DriverTest.php @@ -36,7 +36,7 @@ public function testConnectionDisablesPreparesOnPhp56() self::assertInstanceOf(PDOConnection::class, $connection); try { - self::assertTrue($connection->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES)); + self::assertTrue($connection->getWrappedConnection()->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES)); } catch (PDOException $ignored) { /** @link https://bugs.php.net/bug.php?id=68371 */ $this->markTestIncomplete('See https://bugs.php.net/bug.php?id=68371'); @@ -63,7 +63,10 @@ public function testConnectionDoesNotDisablePreparesOnPhp56WhenAttributeDefined( self::assertInstanceOf(PDOConnection::class, $connection); try { - self::assertNotSame(true, $connection->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES)); + self::assertNotSame( + true, + $connection->getWrappedConnection()->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES) + ); } catch (PDOException $ignored) { /** @link https://bugs.php.net/bug.php?id=68371 */ $this->markTestIncomplete('See https://bugs.php.net/bug.php?id=68371'); @@ -90,7 +93,7 @@ public function testConnectionDisablePreparesOnPhp56WhenDisablePreparesIsExplici self::assertInstanceOf(PDOConnection::class, $connection); try { - self::assertTrue($connection->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES)); + self::assertTrue($connection->getWrappedConnection()->getAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES)); } catch (PDOException $ignored) { /** @link https://bugs.php.net/bug.php?id=68371 */ $this->markTestIncomplete('See https://bugs.php.net/bug.php?id=68371'); diff --git a/tests/Doctrine/Tests/DBAL/DriverManagerTest.php b/tests/Doctrine/Tests/DBAL/DriverManagerTest.php index e5520afcf1e..3fc4d872388 100644 --- a/tests/Doctrine/Tests/DBAL/DriverManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/DriverManagerTest.php @@ -4,7 +4,6 @@ use Doctrine\DBAL\Connections\MasterSlaveConnection; use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver as DrizzlePDOMySqlDriver; use Doctrine\DBAL\Driver\PDOMySql\Driver as PDOMySQLDriver; use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSqliteDriver; use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver; @@ -346,17 +345,17 @@ public function databaseUrls() ], ], 'simple URL with fallthrough scheme containing underscores fails' => [ - 'drizzle_pdo_mysql://foo:bar@localhost/baz', + 'pdo_mysql://foo:bar@localhost/baz', false, ], 'simple URL with fallthrough scheme containing dashes works' => [ - 'drizzle-pdo-mysql://foo:bar@localhost/baz', + 'pdo-mysql://foo:bar@localhost/baz', [ 'user' => 'foo', 'password' => 'bar', 'host' => 'localhost', 'dbname' => 'baz', - 'driver' => DrizzlePDOMySqlDriver::class, + 'driver' => PDOMySQLDriver::class, ], ], 'simple URL with percent encoding' => [ diff --git a/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php index b9d03d5e66c..0850c77d956 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php @@ -6,9 +6,7 @@ use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Driver\Connection as DriverConnection; use Doctrine\DBAL\DriverManager; -use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DbalFunctionalTestCase; use Error; use Exception; @@ -246,17 +244,6 @@ public function testTransactionalReturnValue() self::assertEquals(42, $res); } - /** - * Tests that the quote function accepts DBAL and PDO types. - */ - public function testQuote() - { - self::assertEquals( - $this->connection->quote('foo', Type::STRING), - $this->connection->quote('foo', ParameterType::STRING) - ); - } - public function testPingDoesTriggersConnect() { self::assertTrue($this->connection->ping()); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php index 1c3226852d6..22f8c35d6ac 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/OCI8/OCI8ConnectionTest.php @@ -47,6 +47,6 @@ public function testLastInsertIdAcceptsFqn() $schema = $this->connection->getDatabase(); $sequence = $platform->getIdentitySequenceName($schema . '.DBAL2595', 'id'); - self::assertSame(1, $this->driverConnection->lastInsertId($sequence)); + self::assertSame('1', $this->driverConnection->lastInsertId($sequence)); } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php index 214f90700c1..6cb36067797 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOConnectionTest.php @@ -74,7 +74,9 @@ public function testThrowsWrappedExceptionOnPrepare() // Emulated prepared statements have to be disabled for this test // so that PDO actually communicates with the database server to check the query. - $this->driverConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $this->driverConnection + ->getWrappedConnection() + ->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $this->driverConnection->prepare('foo'); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOSqlsrv/DriverTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOSqlsrv/DriverTest.php index f125eab3515..023590483ad 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOSqlsrv/DriverTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/PDOSqlsrv/DriverTest.php @@ -3,9 +3,11 @@ namespace Doctrine\Tests\DBAL\Functional\Driver\PDOSqlsrv; use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Driver\PDOConnection; use Doctrine\DBAL\Driver\PDOSqlsrv\Driver; use Doctrine\Tests\DBAL\Functional\Driver\AbstractDriverTest; use PDO; +use function assert; use function extension_loaded; class DriverTest extends AbstractDriverTest @@ -69,6 +71,13 @@ public function testDriverOptions() : void { $connection = $this->getConnection([PDO::ATTR_CASE => PDO::CASE_UPPER]); - self::assertSame(PDO::CASE_UPPER, $connection->getAttribute(PDO::ATTR_CASE)); + assert($connection instanceof PDOConnection); + + self::assertSame( + PDO::CASE_UPPER, + $connection + ->getWrappedConnection() + ->getAttribute(PDO::ATTR_CASE) + ); } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/SQLSrv/StatementTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/SQLSrv/StatementTest.php index 38df9cb5181..ab39ff5df38 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/SQLSrv/StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/SQLSrv/StatementTest.php @@ -27,7 +27,7 @@ protected function setUp() public function testFailureToPrepareResultsInException() { // use the driver connection directly to avoid having exception wrapped - $stmt = $this->connection->getWrappedConnection()->prepare(null); + $stmt = $this->connection->getWrappedConnection()->prepare(''); // it's impossible to prepare the statement without bound variables for SQL Server, // so the preparation happens before the first execution when variables are already in place diff --git a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php index e420000ba58..a7810af9485 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php @@ -314,6 +314,7 @@ public function testConnectionExceptionSqLite($mode, $exceptionClass) $table->addColumn('id', 'integer'); $this->expectException($exceptionClass); + foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) { $conn->exec($sql); } @@ -337,10 +338,6 @@ public function testConnectionException($params) $this->markTestSkipped('Only skipped if platform is not sqlite'); } - if ($this->connection->getDatabasePlatform()->getName() === 'drizzle') { - $this->markTestSkipped('Drizzle does not always support authentication'); - } - if ($this->connection->getDatabasePlatform()->getName() === 'postgresql' && isset($params['password'])) { $this->markTestSkipped('Does not work on Travis'); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/DrizzleSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/DrizzleSchemaManagerTest.php deleted file mode 100644 index 070e2173d63..00000000000 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/DrizzleSchemaManagerTest.php +++ /dev/null @@ -1,48 +0,0 @@ -addColumn('id', 'integer'); - $table->addColumn('column_varbinary', 'binary', []); - $table->addColumn('column_binary', 'binary', ['fixed' => true]); - $table->setPrimaryKey(['id']); - - $this->schemaManager->createTable($table); - - $table = $this->schemaManager->listTableDetails($tableName); - - self::assertInstanceOf(BinaryType::class, $table->getColumn('column_varbinary')->getType()); - self::assertFalse($table->getColumn('column_varbinary')->getFixed()); - - self::assertInstanceOf(BinaryType::class, $table->getColumn('column_binary')->getType()); - self::assertFalse($table->getColumn('column_binary')->getFixed()); - } - - public function testColumnCollation() - { - $table = new Table('test_collation'); - $table->addOption('collate', $collation = 'utf8_unicode_ci'); - $table->addColumn('id', 'integer'); - $table->addColumn('text', 'text'); - $table->addColumn('foo', 'text')->setPlatformOption('collation', 'utf8_swedish_ci'); - $table->addColumn('bar', 'text')->setPlatformOption('collation', 'utf8_general_ci'); - $this->schemaManager->dropAndCreateTable($table); - - $columns = $this->schemaManager->listTableColumns('test_collation'); - - self::assertArrayNotHasKey('collation', $columns['id']->getPlatformOptions()); - self::assertEquals('utf8_unicode_ci', $columns['text']->getPlatformOption('collation')); - self::assertEquals('utf8_swedish_ci', $columns['foo']->getPlatformOption('collation')); - self::assertEquals('utf8_general_ci', $columns['bar']->getPlatformOption('collation')); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php index 1c0afc93e66..e7dfde2f4cc 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SchemaManagerFunctionalTestCase.php @@ -976,7 +976,7 @@ protected function createTestTable($name = 'test_table', array $data = []) protected function getTestTable($name, $options = []) { - $table = new Table($name, [], [], [], false, $options); + $table = new Table($name, [], [], [], [], $options); $table->setSchemaConfig($this->schemaManager->createSchemaConfig()); $table->addColumn('id', 'integer', ['notnull' => true]); $table->setPrimaryKey(['id']); @@ -987,7 +987,7 @@ protected function getTestTable($name, $options = []) protected function getTestCompositeTable($name) { - $table = new Table($name, [], [], [], false, []); + $table = new Table($name, [], [], [], [], []); $table->setSchemaConfig($this->schemaManager->createSchemaConfig()); $table->addColumn('id', 'integer', ['notnull' => true]); $table->addColumn('other_id', 'integer', ['notnull' => true]); @@ -999,14 +999,17 @@ protected function getTestCompositeTable($name) protected function assertHasTable($tables, $tableName) { $foundTable = false; + foreach ($tables as $table) { self::assertInstanceOf(Table::class, $table, 'No Table instance was found in tables array.'); + if (strtolower($table->getName()) !== 'list_tables_test_new_name') { continue; } $foundTable = true; } + self::assertTrue($foundTable, 'Could not find new table'); } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL421Test.php b/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL421Test.php deleted file mode 100644 index af41670690a..00000000000 --- a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL421Test.php +++ /dev/null @@ -1,56 +0,0 @@ -connection->getDatabasePlatform()->getName(); - if (in_array($platform, ['mysql', 'sqlite'])) { - return; - } - - $this->markTestSkipped('Currently restricted to MySQL and SQLite.'); - } - - public function testGuidShouldMatchPattern() - { - $guid = $this->connection->query($this->getSelectGuidSql())->fetchColumn(); - $pattern = '/[0-9A-F]{8}\-[0-9A-F]{4}\-[0-9A-F]{4}\-[8-9A-B][0-9A-F]{3}\-[0-9A-F]{12}/i'; - self::assertEquals(1, preg_match($pattern, $guid), 'GUID does not match pattern'); - } - - /** - * This test does (of course) not proof that all generated GUIDs are - * random, it should however provide some basic confidence. - */ - public function testGuidShouldBeRandom() - { - $statement = $this->connection->prepare($this->getSelectGuidSql()); - $guids = []; - - for ($i = 0; $i < 99; $i++) { - $statement->execute(); - $guid = $statement->fetchColumn(); - self::assertNotContains($guid, $guids, 'Duplicate GUID detected'); - $guids[] = $guid; - } - - $statement->closeCursor(); - } - - private function getSelectGuidSql() - { - return 'SELECT ' . $this->connection->getDatabasePlatform()->getGuidExpression(); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php b/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php index 827a9124277..7ba250cd13e 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Ticket/DBAL630Test.php @@ -37,7 +37,9 @@ protected function setUp() protected function tearDown() { if ($this->running) { - $this->connection->getWrappedConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); + $this->connection->getWrappedConnection() + ->getWrappedConnection() + ->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); } parent::tearDown(); @@ -71,7 +73,9 @@ public function testBooleanConversionBoolParamRealPrepares() public function testBooleanConversionBoolParamEmulatedPrepares() { - $this->connection->getWrappedConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $this->connection->getWrappedConnection() + ->getWrappedConnection() + ->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $platform = $this->connection->getDatabasePlatform(); @@ -95,7 +99,9 @@ public function testBooleanConversionNullParamEmulatedPrepares( $statementValue, $databaseConvertedValue ) { - $this->connection->getWrappedConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $this->connection->getWrappedConnection() + ->getWrappedConnection() + ->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $platform = $this->connection->getDatabasePlatform(); @@ -119,7 +125,9 @@ public function testBooleanConversionNullParamEmulatedPreparesWithBooleanTypeInB $statementValue, $databaseConvertedValue ) { - $this->connection->getWrappedConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); + $this->connection->getWrappedConnection() + ->getWrappedConnection() + ->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $platform = $this->connection->getDatabasePlatform(); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php b/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php index d17d5250072..f911ab6146a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Types/BinaryTest.php @@ -7,11 +7,10 @@ use Doctrine\DBAL\Driver\IBMDB2\DB2Driver; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DbalFunctionalTestCase; -use function is_resource; use function random_bytes; use function str_replace; -use function stream_get_contents; class BinaryTest extends DbalFunctionalTestCase { @@ -74,14 +73,6 @@ private function select(string $id) [ParameterType::BINARY] ); - // Currently, `BinaryType` mistakenly converts string values fetched from the DB to a stream. - // It should be the opposite. Streams should be used to represent large objects, not binary - // strings. The confusion comes from the PostgreSQL's type system where binary strings and - // large objects are represented by the same BYTEA type - if (is_resource($value)) { - $value = stream_get_contents($value); - } - - return $value; + return Type::getType('binary')->convertToPHPValue($value, $this->connection->getDatabasePlatform()); } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php index 039fed14f8e..8a108b37f6b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/WriteTest.php @@ -3,12 +3,14 @@ namespace Doctrine\Tests\DBAL\Functional; use DateTime; +use Doctrine\DBAL\Driver\DriverException; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DbalFunctionalTestCase; +use Doctrine\Tests\TestUtil; use Throwable; use function array_filter; use function strtolower; @@ -146,15 +148,22 @@ public function testUpdate() public function testLastInsertId() { - if (! $this->connection->getDatabasePlatform()->prefersIdentityColumns()) { + if (! $this->connection->getDatabasePlatform()->supportsIdentityColumns()) { $this->markTestSkipped('Test only works on platforms with identity columns.'); } self::assertEquals(1, $this->connection->insert('write_table', ['test_int' => 2, 'test_string' => 'bar'])); $num = $this->connection->lastInsertId(); - self::assertNotNull($num, 'LastInsertId() should not be null.'); - self::assertGreaterThan(0, $num, 'LastInsertId() should be non-negative number.'); + self::assertGreaterThan(0, $num, 'lastInsertId() should return a positive number.'); + } + + public function testLastInsertIdNewConnection() + { + $connection = TestUtil::getConnection(); + + $this->expectException(DriverException::class); + $connection->lastInsertId(); } public function testLastInsertIdSequence() @@ -189,7 +198,14 @@ public function testLastInsertIdNoSequenceGiven() $this->markTestSkipped("Test only works consistently on platforms that support sequences and don't support identity columns."); } - self::assertFalse($this->connection->lastInsertId(null)); + $this->expectException(DriverException::class); + $this->connection->lastInsertId(null); + } + + public function testLastInsertIdSequencesNotSupportedOrSequenceDoesNotExist() + { + $this->expectException(DriverException::class); + $this->connection->lastInsertId('unknown-sequence'); } /** diff --git a/tests/Doctrine/Tests/DBAL/Logging/DebugStackTest.php b/tests/Doctrine/Tests/DBAL/Logging/DebugStackTest.php index 914351c75dc..cc12a3234d9 100644 --- a/tests/Doctrine/Tests/DBAL/Logging/DebugStackTest.php +++ b/tests/Doctrine/Tests/DBAL/Logging/DebugStackTest.php @@ -27,8 +27,8 @@ public function testLoggedQuery() [ 1 => [ 'sql' => 'SELECT column FROM table', - 'params' => null, - 'types' => null, + 'params' => [], + 'types' => [], 'executionMS' => 0, ], ], diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php index 63148215f22..160b2b98bfb 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractMySQLPlatformTestCase.php @@ -509,27 +509,21 @@ public function testReturnsBinaryTypeDeclarationSQL() self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL([])); self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0])); self::assertSame('VARBINARY(65535)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 65535])); + self::assertSame('VARBINARY(65536)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 65536])); self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true])); - self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0])); - self::assertSame('BINARY(65535)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 65535])); - } - - /** - * @group legacy - * @expectedDeprecation Binary field length 65536 is greater than supported by the platform (65535). Reduce the field length or use a BLOB field instead. - * @expectedDeprecation Binary field length 16777215 is greater than supported by the platform (65535). Reduce the field length or use a BLOB field instead. - * @expectedDeprecation Binary field length 16777216 is greater than supported by the platform (65535). Reduce the field length or use a BLOB field instead. - */ - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - self::assertSame('MEDIUMBLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 65536])); - self::assertSame('MEDIUMBLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 16777215])); - self::assertSame('LONGBLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 16777216])); - - self::assertSame('MEDIUMBLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 65536])); - self::assertSame('MEDIUMBLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 16777215])); - self::assertSame('LONGBLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 16777216])); + self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 0, + ])); + self::assertSame('BINARY(65535)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 65535, + ])); + self::assertSame('BINARY(65536)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 65536, + ])); } public function testDoesNotPropagateForeignKeyCreationForNonSupportingEngines() diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php index 8fee20fede2..af23b3a3af3 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractPlatformTestCase.php @@ -14,6 +14,7 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\UniqueConstraint; use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DbalTestCase; use Doctrine\Tests\Types\CommentedType; @@ -212,9 +213,9 @@ abstract public function getGenerateUniqueIndexSql(); public function testGeneratesPartialIndexesSqlOnlyWhenSupportingPartialIndexes() { - $where = 'test IS NULL AND test2 IS NOT NULL'; - $indexDef = new Index('name', ['test', 'test2'], false, false, [], ['where' => $where]); - $uniqueIndex = new Index('name', ['test', 'test2'], true, false, [], ['where' => $where]); + $where = 'test IS NULL AND test2 IS NOT NULL'; + $indexDef = new Index('name', ['test', 'test2'], false, false, [], ['where' => $where]); + $uniqueConstraint = new UniqueConstraint('name', ['test', 'test2'], [], []); $expected = ' WHERE ' . $where; @@ -224,15 +225,14 @@ public function testGeneratesPartialIndexesSqlOnlyWhenSupportingPartialIndexes() $actuals[] = $this->platform->getIndexDeclarationSQL('name', $indexDef); } - $actuals[] = $this->platform->getUniqueConstraintDeclarationSQL('name', $uniqueIndex); - $actuals[] = $this->platform->getCreateIndexSQL($indexDef, 'table'); + $uniqueConstraintSQL = $this->platform->getUniqueConstraintDeclarationSQL('name', $uniqueConstraint); + $indexSQL = $this->platform->getCreateIndexSQL($indexDef, 'table'); - foreach ($actuals as $actual) { - if ($this->platform->supportsPartialIndexes()) { - self::assertStringEndsWith($expected, $actual, 'WHERE clause should be present'); - } else { - self::assertStringEndsNotWith($expected, $actual, 'WHERE clause should NOT be present'); - } + $this->assertStringEndsNotWith($expected, $uniqueConstraintSQL, 'WHERE clause should NOT be present'); + if ($this->platform->supportsPartialIndexes()) { + self::assertStringEndsWith($expected, $indexSQL, 'WHERE clause should be present'); + } else { + self::assertStringEndsNotWith($expected, $indexSQL, 'WHERE clause should NOT be present'); } } @@ -701,11 +701,11 @@ public function testQuotedColumnInForeignKeyPropagation() */ public function testQuotesReservedKeywordInUniqueConstraintDeclarationSQL() { - $index = new Index('select', ['foo'], true); + $constraint = new UniqueConstraint('select', ['foo'], [], []); self::assertSame( $this->getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(), - $this->platform->getUniqueConstraintDeclarationSQL('select', $index) + $this->platform->getUniqueConstraintDeclarationSQL('select', $constraint) ); } @@ -848,11 +848,6 @@ public function testReturnsBinaryTypeDeclarationSQL() $this->platform->getBinaryTypeDeclarationSQL([]); } - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - $this->markTestSkipped('Not applicable to the platform'); - } - /** * @group DBAL-553 */ diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php index 1341c9e8819..db7b9556cb3 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php @@ -985,18 +985,14 @@ public function testReturnsBinaryTypeDeclarationSQL() self::assertSame('VARBINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8000])); self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true])); - self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0])); - self::assertSame('BINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8000])); - } - - /** - * @group legacy - * @expectedDeprecation Binary field length 8001 is greater than supported by the platform (8000). Reduce the field length or use a BLOB field instead. - */ - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8001])); - self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8001])); + self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 0, + ])); + self::assertSame('BINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 8000, + ])); } /** @@ -1370,7 +1366,7 @@ public function getReturnsForeignKeyReferentialActionSQL() */ protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() { - return 'CONSTRAINT [select] UNIQUE (foo) WHERE foo IS NOT NULL'; + return 'CONSTRAINT [select] UNIQUE (foo)'; } /** diff --git a/tests/Doctrine/Tests/DBAL/Platforms/DB2PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/DB2PlatformTest.php index ce2b7df42a8..5b52552e81a 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/DB2PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/DB2PlatformTest.php @@ -342,7 +342,7 @@ public function testModifiesLimitQuery() { self::assertEquals( 'SELECT * FROM user', - $this->platform->modifyLimitQuery('SELECT * FROM user', null, null) + $this->platform->modifyLimitQuery('SELECT * FROM user', null, 0) ); self::assertEquals( @@ -415,16 +415,6 @@ public function testReturnsBinaryTypeDeclarationSQL() self::assertSame('CHAR(254) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0])); } - /** - * @group legacy - * @expectedDeprecation Binary field length 32705 is greater than supported by the platform (32704). Reduce the field length or use a BLOB field instead. - */ - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32705])); - self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32705])); - } - /** * @group DBAL-234 */ diff --git a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php index e8f8f0cdd7f..3cfdb7bef09 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php @@ -481,18 +481,14 @@ public function testReturnsBinaryTypeDeclarationSQL() self::assertSame('RAW(2000)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 2000])); self::assertSame('RAW(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true])); - self::assertSame('RAW(2000)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0])); - self::assertSame('RAW(2000)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 2000])); - } - - /** - * @group legacy - * @expectedDeprecation Binary field length 2001 is greater than supported by the platform (2000). Reduce the field length or use a BLOB field instead. - */ - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 2001])); - self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 2001])); + self::assertSame('RAW(2000)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 0, + ])); + self::assertSame('RAW(2000)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 2000, + ])); } public function testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType() diff --git a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL91PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL91PlatformTest.php deleted file mode 100644 index ec62ac8b026..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL91PlatformTest.php +++ /dev/null @@ -1,40 +0,0 @@ -platform->supportsColumnCollation()); - } - - public function testColumnCollationDeclarationSQL() : void - { - self::assertSame( - 'COLLATE "en_US.UTF-8"', - $this->platform->getColumnCollationDeclarationSQL('en_US.UTF-8') - ); - } - - public function testGetCreateTableSQLWithColumnCollation() : void - { - $table = new Table('foo'); - $table->addColumn('no_collation', 'string'); - $table->addColumn('column_collation', 'string')->setPlatformOption('collation', 'en_US.UTF-8'); - - self::assertSame( - ['CREATE TABLE foo (no_collation VARCHAR(255) NOT NULL, column_collation VARCHAR(255) NOT NULL COLLATE "en_US.UTF-8")'], - $this->platform->getCreateTableSQL($table), - 'Column "no_collation" will use the default collation from the table/database and "column_collation" overwrites the collation on this column' - ); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL92PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL92PlatformTest.php deleted file mode 100644 index fb36beb334a..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL92PlatformTest.php +++ /dev/null @@ -1,71 +0,0 @@ -platform->hasNativeJsonType()); - } - - /** - * @group DBAL-553 - */ - public function testReturnsJsonTypeDeclarationSQL() - { - self::assertSame('JSON', $this->platform->getJsonTypeDeclarationSQL([])); - } - - public function testReturnsSmallIntTypeDeclarationSQL() - { - self::assertSame( - 'SMALLSERIAL', - $this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => true]) - ); - - self::assertSame( - 'SMALLINT', - $this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => false]) - ); - - self::assertSame( - 'SMALLINT', - $this->platform->getSmallIntTypeDeclarationSQL([]) - ); - } - - /** - * @group DBAL-553 - */ - public function testInitializesJsonTypeMapping() - { - self::assertTrue($this->platform->hasDoctrineTypeMappingFor('json')); - self::assertEquals(Type::JSON, $this->platform->getDoctrineTypeMapping('json')); - } - - /** - * @group DBAL-1220 - */ - public function testReturnsCloseActiveDatabaseConnectionsSQL() - { - self::assertSame( - "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'foo'", - $this->platform->getCloseActiveDatabaseConnectionsSQL('foo') - ); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL94PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL94PlatformTest.php index ed118aec406..0bcf51e7435 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL94PlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSQL94PlatformTest.php @@ -5,7 +5,7 @@ use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Types\Type; -class PostgreSQL94PlatformTest extends PostgreSQL92PlatformTest +class PostgreSQL94PlatformTest extends PostgreSqlPlatformTest { /** * {@inheritdoc} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php index b45a152ab0a..9fdffcb951e 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/PostgreSqlPlatformTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\DBAL\Platforms; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Types\Type; class PostgreSqlPlatformTest extends AbstractPostgreSqlPlatformTestCase { @@ -15,4 +16,66 @@ public function testSupportsPartialIndexes() { self::assertTrue($this->platform->supportsPartialIndexes()); } + + public function testColumnCollationDeclarationSQL() + { + self::assertEquals( + 'COLLATE "en_US.UTF-8"', + $this->platform->getColumnCollationDeclarationSQL('en_US.UTF-8') + ); + } + + /** + * @group DBAL-553 + */ + public function testHasNativeJsonType() + { + self::assertTrue($this->platform->hasNativeJsonType()); + } + + /** + * @group DBAL-553 + */ + public function testReturnsJsonTypeDeclarationSQL() + { + self::assertSame('JSON', $this->platform->getJsonTypeDeclarationSQL([])); + } + + public function testReturnsSmallIntTypeDeclarationSQL() + { + self::assertSame( + 'SMALLSERIAL', + $this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => true]) + ); + + self::assertSame( + 'SMALLINT', + $this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => false]) + ); + + self::assertSame( + 'SMALLINT', + $this->platform->getSmallIntTypeDeclarationSQL([]) + ); + } + + /** + * @group DBAL-553 + */ + public function testInitializesJsonTypeMapping() + { + self::assertTrue($this->platform->hasDoctrineTypeMappingFor('json')); + self::assertEquals(Type::JSON, $this->platform->getDoctrineTypeMapping('json')); + } + + /** + * @group DBAL-1220 + */ + public function testReturnsCloseActiveDatabaseConnectionsSQL() + { + self::assertSame( + "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'foo'", + $this->platform->getCloseActiveDatabaseConnectionsSQL('foo') + ); + } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere11PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere11PlatformTest.php deleted file mode 100644 index 7e91777d6ef..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere11PlatformTest.php +++ /dev/null @@ -1,26 +0,0 @@ -markTestSkipped('This version of the platform now supports regular expressions.'); - } - - public function testGeneratesRegularExpressionSQLSnippet() - { - self::assertEquals('REGEXP', $this->platform->getRegexpExpression()); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere12PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere12PlatformTest.php deleted file mode 100644 index 7cbb2c69bb8..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere12PlatformTest.php +++ /dev/null @@ -1,141 +0,0 @@ -markTestSkipped('This version of the platform now supports sequences.'); - } - - public function testSupportsSequences() - { - self::assertTrue($this->platform->supportsSequences()); - } - - public function testGeneratesSequenceSqlCommands() - { - $sequence = new Sequence('myseq', 20, 1); - self::assertEquals( - 'CREATE SEQUENCE myseq INCREMENT BY 20 START WITH 1 MINVALUE 1', - $this->platform->getCreateSequenceSQL($sequence) - ); - self::assertEquals( - 'ALTER SEQUENCE myseq INCREMENT BY 20', - $this->platform->getAlterSequenceSQL($sequence) - ); - self::assertEquals( - 'DROP SEQUENCE myseq', - $this->platform->getDropSequenceSQL('myseq') - ); - self::assertEquals( - 'DROP SEQUENCE myseq', - $this->platform->getDropSequenceSQL($sequence) - ); - self::assertEquals( - 'SELECT myseq.NEXTVAL', - $this->platform->getSequenceNextValSQL('myseq') - ); - self::assertEquals( - 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE', - $this->platform->getListSequencesSQL(null) - ); - } - - public function testGeneratesDateTimeTzColumnTypeDeclarationSQL() - { - self::assertEquals( - 'TIMESTAMP WITH TIME ZONE', - $this->platform->getDateTimeTzTypeDeclarationSQL([ - 'length' => 10, - 'fixed' => true, - 'unsigned' => true, - 'autoincrement' => true, - ]) - ); - } - - public function testHasCorrectDateTimeTzFormatString() - { - self::assertEquals('Y-m-d H:i:s.uP', $this->platform->getDateTimeTzFormatString()); - } - - public function testInitializesDateTimeTzTypeMapping() - { - self::assertTrue($this->platform->hasDoctrineTypeMappingFor('timestamp with time zone')); - self::assertEquals('datetime', $this->platform->getDoctrineTypeMapping('timestamp with time zone')); - } - - public function testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL() - { - self::assertEquals( - 'CREATE VIRTUAL UNIQUE CLUSTERED INDEX fooindex ON footable (a, b) WITH NULLS NOT DISTINCT FOR OLAP WORKLOAD', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - true, - false, - ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload'] - ), - 'footable' - ) - ); - self::assertEquals( - 'CREATE VIRTUAL CLUSTERED INDEX fooindex ON footable (a, b) FOR OLAP WORKLOAD', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - false, - ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload'] - ), - 'footable' - ) - ); - - // WITH NULLS NOT DISTINCT clause not available on primary indexes. - self::assertEquals( - 'ALTER TABLE footable ADD PRIMARY KEY (a, b)', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - true, - ['with_nulls_not_distinct'] - ), - 'footable' - ) - ); - - // WITH NULLS NOT DISTINCT clause not available on non-unique indexes. - self::assertEquals( - 'CREATE INDEX fooindex ON footable (a, b)', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - false, - ['with_nulls_not_distinct'] - ), - 'footable' - ) - ); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere16PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere16PlatformTest.php deleted file mode 100644 index 9692ffb1f76..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywhere16PlatformTest.php +++ /dev/null @@ -1,79 +0,0 @@ -platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - true, - false, - ['with_nulls_distinct'] - ), - 'footable' - ) - ); - - // WITH NULLS DISTINCT clause not available on primary indexes. - self::assertEquals( - 'ALTER TABLE footable ADD PRIMARY KEY (a, b)', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - true, - ['with_nulls_distinct'] - ), - 'footable' - ) - ); - - // WITH NULLS DISTINCT clause not available on non-unique indexes. - self::assertEquals( - 'CREATE INDEX fooindex ON footable (a, b)', - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - false, - ['with_nulls_distinct'] - ), - 'footable' - ) - ); - - parent::testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL(); - } - - public function testThrowsExceptionOnInvalidWithNullsNotDistinctIndexOptions() - { - $this->expectException('UnexpectedValueException'); - - $this->platform->getCreateIndexSQL( - new Index( - 'fooindex', - ['a', 'b'], - false, - false, - ['with_nulls_distinct', 'with_nulls_not_distinct'] - ), - 'footable' - ); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php index 294b9836e2e..890e9d6f1d3 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php @@ -13,8 +13,10 @@ use Doctrine\DBAL\Schema\Constraint; use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\UniqueConstraint; use Doctrine\DBAL\TransactionIsolationLevel; use Doctrine\DBAL\Types\Type; use InvalidArgumentException; @@ -359,12 +361,12 @@ public function testGeneratesUniqueConstraintDeclarationSQL() 'CONSTRAINT unique_constraint UNIQUE CLUSTERED (a, b)', $this->platform->getUniqueConstraintDeclarationSQL( 'unique_constraint', - new Index(null, ['a', 'b'], true, false, ['clustered']) + new UniqueConstraint(null, ['a', 'b'], ['clustered']) ) ); self::assertEquals( - 'UNIQUE (a, b)', - $this->platform->getUniqueConstraintDeclarationSQL(null, new Index(null, ['a', 'b'], true, false)) + 'CONSTRAINT UNIQUE (a, b)', + $this->platform->getUniqueConstraintDeclarationSQL(null, new UniqueConstraint(null, ['a', 'b'])) ); } @@ -372,7 +374,7 @@ public function testCannotGenerateUniqueConstraintDeclarationSQLWithEmptyColumns { $this->expectException(InvalidArgumentException::class); - $this->platform->getUniqueConstraintDeclarationSQL('constr', new Index('constr', [], true)); + $this->platform->getUniqueConstraintDeclarationSQL('constr', new UniqueConstraint('constr', [])); } public function testGeneratesForeignKeyConstraintsWithAdvancedPlatformOptionsSQL() @@ -452,20 +454,123 @@ public function testCannotGenerateCustomConstraintWithCreateConstraintSQL() public function testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL() { self::assertEquals( - 'CREATE VIRTUAL UNIQUE CLUSTERED INDEX fooindex ON footable (a, b) FOR OLAP WORKLOAD', + 'CREATE UNIQUE INDEX fooindex ON footable (a, b) WITH NULLS DISTINCT', $this->platform->getCreateIndexSQL( new Index( 'fooindex', ['a', 'b'], true, false, - ['virtual', 'clustered', 'for_olap_workload'] + ['with_nulls_distinct'] + ), + 'footable' + ) + ); + + // WITH NULLS DISTINCT clause not available on primary indexes. + self::assertEquals( + 'ALTER TABLE footable ADD PRIMARY KEY (a, b)', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + true, + ['with_nulls_distinct'] + ), + 'footable' + ) + ); + + // WITH NULLS DISTINCT clause not available on non-unique indexes. + self::assertEquals( + 'CREATE INDEX fooindex ON footable (a, b)', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + false, + ['with_nulls_distinct'] + ), + 'footable' + ) + ); + + self::assertEquals( + 'CREATE VIRTUAL UNIQUE CLUSTERED INDEX fooindex ON footable (a, b) WITH NULLS NOT DISTINCT FOR OLAP WORKLOAD', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + true, + false, + ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload'] + ), + 'footable' + ) + ); + self::assertEquals( + 'CREATE VIRTUAL CLUSTERED INDEX fooindex ON footable (a, b) FOR OLAP WORKLOAD', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + false, + ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload'] + ), + 'footable' + ) + ); + + // WITH NULLS NOT DISTINCT clause not available on primary indexes. + self::assertEquals( + 'ALTER TABLE footable ADD PRIMARY KEY (a, b)', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + true, + ['with_nulls_not_distinct'] + ), + 'footable' + ) + ); + + // WITH NULLS NOT DISTINCT clause not available on non-unique indexes. + self::assertEquals( + 'CREATE INDEX fooindex ON footable (a, b)', + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + false, + ['with_nulls_not_distinct'] ), 'footable' ) ); } + public function testThrowsExceptionOnInvalidWithNullsNotDistinctIndexOptions() + { + $this->expectException('UnexpectedValueException'); + + $this->platform->getCreateIndexSQL( + new Index( + 'fooindex', + ['a', 'b'], + false, + false, + ['with_nulls_distinct', 'with_nulls_not_distinct'] + ), + 'footable' + ); + } + public function testDoesNotSupportIndexDeclarationInCreateAlterTableStatements() { $this->expectException(DBALException::class); @@ -530,7 +635,6 @@ public function testGeneratesSQLSnippets() self::assertEquals('Y-m-d H:i:s.u', $this->platform->getDateTimeFormatString()); self::assertEquals('H:i:s.u', $this->platform->getTimeFormatString()); self::assertEquals('', $this->platform->getForUpdateSQL()); - self::assertEquals('NEWID()', $this->platform->getGuidExpression()); self::assertEquals('LOCATE(string_column, substring_column)', $this->platform->getLocateExpression('string_column', 'substring_column')); self::assertEquals('LOCATE(string_column, substring_column, 1)', $this->platform->getLocateExpression('string_column', 'substring_column', 1)); self::assertEquals("HASH(column, 'MD5')", $this->platform->getMd5Expression('column')); @@ -573,19 +677,28 @@ public function testGeneratesSQLSnippets() ); } - public function testDoesNotSupportRegexp() + public function testHasCorrectDateTimeTzFormatString() { - $this->expectException(DBALException::class); + self::assertEquals('Y-m-d H:i:s.uP', $this->platform->getDateTimeTzFormatString()); + } - $this->platform->getRegexpExpression(); + public function testGeneratesDateTimeTzColumnTypeDeclarationSQL() + { + self::assertEquals( + 'TIMESTAMP WITH TIME ZONE', + $this->platform->getDateTimeTzTypeDeclarationSQL([ + 'length' => 10, + 'fixed' => true, + 'unsigned' => true, + 'autoincrement' => true, + ]) + ); } - public function testHasCorrectDateTimeTzFormatString() + public function testInitializesDateTimeTzTypeMapping() { - // Date time type with timezone is not supported before version 12. - // For versions before we have to ensure that the date time with timezone format - // equals the normal date time format so that it corresponds to the declaration SQL equality (datetimetz -> datetime). - self::assertEquals($this->platform->getDateTimeFormatString(), $this->platform->getDateTimeTzFormatString()); + self::assertTrue($this->platform->hasDoctrineTypeMappingFor('timestamp with time zone')); + self::assertEquals('datetime', $this->platform->getDoctrineTypeMapping('timestamp with time zone')); } public function testHasCorrectDefaultTransactionIsolationLevel() @@ -647,7 +760,7 @@ public function testModifiesLimitQueryWithOffset() ); self::assertEquals( 'SELECT TOP ALL START AT 6 * FROM user', - $this->platform->modifyLimitQuery('SELECT * FROM user', 0, 5) + $this->platform->modifyLimitQuery('SELECT * FROM user', null, 5) ); } @@ -736,7 +849,41 @@ public function testSupportsGettingAffectedRows() public function testDoesNotSupportSequences() { - self::assertFalse($this->platform->supportsSequences()); + self::markTestSkipped('This version of the platform now supports sequences.'); + } + + public function testSupportsSequences() + { + self::assertTrue($this->platform->supportsSequences()); + } + + public function testGeneratesSequenceSqlCommands() + { + $sequence = new Sequence('myseq', 20, 1); + self::assertEquals( + 'CREATE SEQUENCE myseq INCREMENT BY 20 START WITH 1 MINVALUE 1', + $this->platform->getCreateSequenceSQL($sequence) + ); + self::assertEquals( + 'ALTER SEQUENCE myseq INCREMENT BY 20', + $this->platform->getAlterSequenceSQL($sequence) + ); + self::assertEquals( + 'DROP SEQUENCE myseq', + $this->platform->getDropSequenceSQL('myseq') + ); + self::assertEquals( + 'DROP SEQUENCE myseq', + $this->platform->getDropSequenceSQL($sequence) + ); + self::assertEquals( + 'SELECT myseq.NEXTVAL', + $this->platform->getSequenceNextValSQL('myseq') + ); + self::assertEquals( + 'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE', + $this->platform->getListSequencesSQL(null) + ); } public function testDoesNotSupportInlineColumnComments() @@ -778,18 +925,14 @@ public function testReturnsBinaryTypeDeclarationSQL() self::assertSame('VARBINARY(32767)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32767])); self::assertSame('BINARY(1)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true])); - self::assertSame('BINARY(1)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0])); - self::assertSame('BINARY(32767)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32767])); - } - - /** - * @group legacy - * @expectedDeprecation Binary field length 32768 is greater than supported by the platform (32767). Reduce the field length or use a BLOB field instead. - */ - public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() - { - self::assertSame('LONG BINARY', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32768])); - self::assertSame('LONG BINARY', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32768])); + self::assertSame('BINARY(1)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 0, + ])); + self::assertSame('BINARY(32767)', $this->platform->getBinaryTypeDeclarationSQL([ + 'fixed' => true, + 'length' => 32767, + ])); } /** diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2008PlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2008PlatformTest.php deleted file mode 100644 index 3083ab7b606..00000000000 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLServer2008PlatformTest.php +++ /dev/null @@ -1,18 +0,0 @@ -platform->getDateTimeTzTypeDeclarationSQL([])); - } -} diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php index ed2842813aa..f2f72719eff 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SQLServerPlatformTest.php @@ -53,7 +53,7 @@ public function getModifyLimitQueries() [ 'SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_ ORDER BY c0_.title ASC) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', 30, - null, + 0, 'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum <= 30 ORDER BY doctrine_rownum ASC', ], @@ -61,9 +61,14 @@ public function getModifyLimitQueries() [ 'SELECT id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC', 30, - null, + 0, 'WITH dctrn_cte AS (SELECT TOP 30 id_0, MIN(sclr_2) AS dctrn_minrownum FROM (SELECT c0_.id AS id_0, c0_.title AS title_1, ROW_NUMBER() OVER(ORDER BY c0_.title ASC) AS sclr_2 FROM TestTable c0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum <= 30 ORDER BY doctrine_rownum ASC', ], ]; } + + public function testGeneratesTypeDeclarationForDateTimeTz() + { + self::assertEquals('DATETIMEOFFSET(6)', $this->platform->getDateTimeTzTypeDeclarationSQL([])); + } } diff --git a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php index fdd28d87847..a865cf39546 100644 --- a/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/DBAL/Query/QueryBuilderTest.php @@ -411,6 +411,16 @@ public function testUpdateWithoutAlias() self::assertEquals('UPDATE users SET foo = ?, bar = ?', (string) $qb); } + public function testUpdateWithMatchingAlias() + { + $qb = new QueryBuilder($this->conn); + $qb->update('users', 'users') + ->set('foo', '?') + ->set('bar', '?'); + + self::assertEquals('UPDATE users SET foo = ?, bar = ?', (string) $qb); + } + public function testUpdateWhere() { $qb = new QueryBuilder($this->conn); @@ -448,6 +458,15 @@ public function testDeleteWithoutAlias() self::assertEquals('DELETE FROM users', (string) $qb); } + public function testDeleteWithMatchingAlias() + { + $qb = new QueryBuilder($this->conn); + $qb->delete('users', 'users'); + + self::assertEquals(QueryBuilder::DELETE, $qb->getType()); + self::assertEquals('DELETE FROM users', (string) $qb); + } + public function testDeleteWhere() { $qb = new QueryBuilder($this->conn); @@ -776,6 +795,16 @@ public function testSimpleSelectWithoutTableAlias() self::assertEquals('SELECT id FROM users', (string) $qb); } + public function testSimpleSelectWithMatchingTableAlias() + { + $qb = new QueryBuilder($this->conn); + + $qb->select('id') + ->from('users', 'users'); + + self::assertEquals('SELECT id FROM users', (string) $qb); + } + public function testSelectWithSimpleWhereWithoutTableAlias() { $qb = new QueryBuilder($this->conn); diff --git a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php index d8b0c375801..b5e13ef02d5 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php @@ -1272,6 +1272,7 @@ public function testForeignKeyRemovalWithRenamedLocalColumn() 'id_table1' => new Column('id_table1', Type::getType('integer')), ], [], + [], [ new ForeignKeyConstraint(['id_table1'], 'table1', ['id'], 'fk_table2_table1'), ] @@ -1285,6 +1286,7 @@ public function testForeignKeyRemovalWithRenamedLocalColumn() 'id_table3' => new Column('id_table3', Type::getType('integer')), ], [], + [], [ new ForeignKeyConstraint(['id_table3'], 'table3', ['id'], 'fk_table2_table3'), ] diff --git a/tests/Doctrine/Tests/DBAL/Schema/TableTest.php b/tests/Doctrine/Tests/DBAL/Schema/TableTest.php index ac5fead5bcf..cea88792394 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/TableTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/TableTest.php @@ -200,7 +200,7 @@ public function testConstraints() { $constraint = new ForeignKeyConstraint([], 'foo', []); - $tableA = new Table('foo', [], [], [$constraint]); + $tableA = new Table('foo', [], [], [], [$constraint]); $constraints = $tableA->getForeignKeys(); self::assertCount(1, $constraints); @@ -209,7 +209,7 @@ public function testConstraints() public function testOptions() { - $table = new Table('foo', [], [], [], false, ['foo' => 'bar']); + $table = new Table('foo', [], [], [], [], ['foo' => 'bar']); self::assertTrue($table->hasOption('foo')); self::assertEquals('bar', $table->getOption('foo')); diff --git a/tests/Doctrine/Tests/DBAL/StatementTest.php b/tests/Doctrine/Tests/DBAL/StatementTest.php index 7f18b20936a..a9e977035ac 100644 --- a/tests/Doctrine/Tests/DBAL/StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/StatementTest.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Driver\Connection as DriverConnection; +use Doctrine\DBAL\Driver\Statement as DriverStatement; use Doctrine\DBAL\Logging\SQLLogger; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Statement; @@ -23,26 +24,23 @@ class StatementTest extends DbalTestCase private $configuration; /** @var PDOStatement */ - private $pdoStatement; + private $driverStatement; protected function setUp() { - $this->pdoStatement = $this->getMockBuilder(PDOStatement::class) - ->setMethods(['execute', 'bindParam', 'bindValue']) - ->getMock(); - $platform = new MockPlatform(); - $driverConnection = $this->createMock(DriverConnection::class); - $driverConnection->expects($this->any()) - ->method('prepare') - ->will($this->returnValue($this->pdoStatement)); - - $driver = $this->createMock(Driver::class); - $constructorArgs = [ - ['platform' => $platform], - $driver, - ]; - $this->conn = $this->getMockBuilder(Connection::class) - ->setConstructorArgs($constructorArgs) + $this->driverStatement = $this->createMock(DriverStatement::class); + + $driverConnection = $this->createConfiguredMock(DriverConnection::class, [ + 'prepare' => $this->driverStatement, + ]); + + $driver = $this->createMock(Driver::class); + + $this->conn = $this->getMockBuilder(Connection::class) + ->setConstructorArgs([ + ['platform' => new MockPlatform()], + $driver, + ]) ->getMock(); $this->conn->expects($this->atLeastOnce()) ->method('getWrappedConnection') @@ -146,7 +144,7 @@ public function testExecuteCallsLoggerStopQueryOnException() $logger->expects($this->once()) ->method('stopQuery'); - $this->pdoStatement->expects($this->once()) + $this->driverStatement->expects($this->once()) ->method('execute') ->will($this->throwException(new Exception('Mock test exception'))); diff --git a/tests/Doctrine/Tests/DBAL/Types/BinaryTest.php b/tests/Doctrine/Tests/DBAL/Types/BinaryTest.php index fbcf5fb9357..27b4f6098f1 100644 --- a/tests/Doctrine/Tests/DBAL/Types/BinaryTest.php +++ b/tests/Doctrine/Tests/DBAL/Types/BinaryTest.php @@ -7,9 +7,11 @@ use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DBAL\Mocks\MockPlatform; use Doctrine\Tests\DbalTestCase; +use function array_map; use function base64_encode; use function fopen; -use function stream_get_contents; +use function implode; +use function range; class BinaryTest extends DbalTestCase { @@ -50,11 +52,10 @@ public function testBinaryNullConvertsToPHPValue() public function testBinaryStringConvertsToPHPValue() { - $databaseValue = 'binary string'; + $databaseValue = $this->getBinaryString(); $phpValue = $this->type->convertToPHPValue($databaseValue, $this->platform); - self::assertInternalType('resource', $phpValue); - self::assertEquals($databaseValue, stream_get_contents($phpValue)); + self::assertSame($databaseValue, $phpValue); } public function testBinaryResourceConvertsToPHPValue() @@ -62,7 +63,15 @@ public function testBinaryResourceConvertsToPHPValue() $databaseValue = fopen('data://text/plain;base64,' . base64_encode('binary string'), 'r'); $phpValue = $this->type->convertToPHPValue($databaseValue, $this->platform); - self::assertSame($databaseValue, $phpValue); + self::assertSame('binary string', $phpValue); + } + + /** + * Creates a binary string containing all possible byte values. + */ + private function getBinaryString() : string + { + return implode(array_map('chr', range(0, 255))); } /** diff --git a/tests/Doctrine/Tests/DBAL/Types/BlobTest.php b/tests/Doctrine/Tests/DBAL/Types/BlobTest.php index 5500836cfdb..045b5cb320d 100644 --- a/tests/Doctrine/Tests/DBAL/Types/BlobTest.php +++ b/tests/Doctrine/Tests/DBAL/Types/BlobTest.php @@ -6,10 +6,6 @@ use Doctrine\DBAL\Types\Type; use Doctrine\Tests\DBAL\Mocks\MockPlatform; use Doctrine\Tests\DbalTestCase; -use function base64_encode; -use function chr; -use function fopen; -use function stream_get_contents; class BlobTest extends DbalTestCase { @@ -32,37 +28,4 @@ public function testBlobNullConvertsToPHPValue() { self::assertNull($this->type->convertToPHPValue(null, $this->platform)); } - - public function testBinaryStringConvertsToPHPValue() - { - $databaseValue = $this->getBinaryString(); - $phpValue = $this->type->convertToPHPValue($databaseValue, $this->platform); - - self::assertInternalType('resource', $phpValue); - self::assertSame($databaseValue, stream_get_contents($phpValue)); - } - - public function testBinaryResourceConvertsToPHPValue() - { - $databaseValue = fopen('data://text/plain;base64,' . base64_encode($this->getBinaryString()), 'r'); - $phpValue = $this->type->convertToPHPValue($databaseValue, $this->platform); - - self::assertSame($databaseValue, $phpValue); - } - - /** - * Creates a binary string containing all possible byte values. - * - * @return string - */ - private function getBinaryString() - { - $string = ''; - - for ($i = 0; $i < 256; $i++) { - $string .= chr($i); - } - - return $string; - } } diff --git a/tests/Doctrine/Tests/DBAL/Types/StringTest.php b/tests/Doctrine/Tests/DBAL/Types/StringTest.php index 74716e4f23c..dea28c2b0f5 100644 --- a/tests/Doctrine/Tests/DBAL/Types/StringTest.php +++ b/tests/Doctrine/Tests/DBAL/Types/StringTest.php @@ -25,11 +25,6 @@ public function testReturnsSqlDeclarationFromPlatformVarchar() self::assertEquals('DUMMYVARCHAR()', $this->type->getSqlDeclaration([], $this->platform)); } - public function testReturnsDefaultLengthFromPlatformVarchar() - { - self::assertEquals(255, $this->type->getDefaultLength($this->platform)); - } - public function testConvertToPHPValue() { self::assertInternalType('string', $this->type->convertToPHPValue('foo', $this->platform)); diff --git a/tests/Doctrine/Tests/Mocks/ConnectionMock.php b/tests/Doctrine/Tests/Mocks/ConnectionMock.php index 157584f09f7..40c266c7340 100644 --- a/tests/Doctrine/Tests/Mocks/ConnectionMock.php +++ b/tests/Doctrine/Tests/Mocks/ConnectionMock.php @@ -3,7 +3,6 @@ namespace Doctrine\Tests\Mocks; use Doctrine\DBAL\Connection; -use function is_string; class ConnectionMock extends Connection { @@ -39,17 +38,14 @@ public function insert($tableName, array $data, array $types = []) $this->inserts[$tableName][] = $data; } - public function lastInsertId($seqName = null) + public function lastInsertId(?string $seqName = null) : string { - return $this->lastInsertId; + return (string) $this->lastInsertId; } - public function quote($input, $type = null) + public function quote(string $input) : string { - if (is_string($input)) { - return "'" . $input . "'"; - } - return $input; + return "'" . $input . "'"; } public function setLastInsertId($id) diff --git a/tests/Doctrine/Tests/Mocks/DriverConnectionMock.php b/tests/Doctrine/Tests/Mocks/DriverConnectionMock.php deleted file mode 100644 index 7b8872f2ea8..00000000000 --- a/tests/Doctrine/Tests/Mocks/DriverConnectionMock.php +++ /dev/null @@ -1,42 +0,0 @@ -