diff --git a/composer.json b/composer.json index 5f1ea1604..f5d0a7515 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "cweagans/composer-patches": "^1.7", "deepdiver/zipstreamer": "^2.0.3", "deepdiver1975/tarstreamer": "^2.1.0", - "doctrine/dbal": "^3.7.0", + "doctrine/dbal": "^4.0.4", "egulias/email-validator": "^4.0.2", "fusonic/opengraph": "^2.3.0", "giggsey/libphonenumber-for-php-lite": "^8.13.12", diff --git a/composer.lock b/composer.lock index a61a52d2a..75f61be2f 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": "4d14ded14d6eb8487843f729ca5ae46a", + "content-hash": "0b41b98db06481530488b64109503731", "packages": [ { "name": "aws/aws-crt-php", @@ -430,142 +430,44 @@ }, "time": "2023-06-16T08:01:55+00:00" }, - { - "name": "doctrine/cache", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", - "shasum": "" - }, - "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", - "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", - "php", - "redis", - "xcache" - ], - "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "time": "2022-05-20T20:07:39+00:00" - }, { "name": "doctrine/dbal", - "version": "3.8.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c" + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c", - "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/50fda19f80724b55ff770bb4ff352407008e63c5", + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5", "shasum": "" }, "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", + "php": "^8.1", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.58", - "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.16", - "psalm/plugin-phpunit": "0.18.4", + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "1.11.5", + "phpstan/phpstan-phpunit": "1.4.0", + "phpstan/phpstan-strict-rules": "^1.6", + "phpunit/phpunit": "10.5.22", + "psalm/plugin-phpunit": "0.19.0", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.9.0", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0", - "vimeo/psalm": "4.30.0" + "squizlabs/php_codesniffer": "3.10.1", + "symfony/cache": "^6.3.8|^7.0", + "symfony/console": "^5.4|^6.3|^7.0", + "vimeo/psalm": "5.24.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "bin": [ - "bin/doctrine-dbal" - ], "type": "library", "autoload": { "psr-4": { @@ -618,7 +520,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.3" + "source": "https://github.com/doctrine/dbal/tree/4.0.4" }, "funding": [ { @@ -634,7 +536,7 @@ "type": "tidelift" } ], - "time": "2024-03-03T15:55:06+00:00" + "time": "2024-06-19T11:57:23+00:00" }, { "name": "doctrine/deprecations", @@ -683,98 +585,6 @@ }, "time": "2024-01-30T19:34:25+00:00" }, - { - "name": "doctrine/event-manager", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.24" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "time": "2022-10-12T20:51:15+00:00" - }, { "name": "doctrine/lexer", "version": "3.0.1", diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 009ccaa7d..b58ae4a95 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -1211,22 +1211,6 @@ 'Cose\\Key\\OkpKey' => $vendorDir . '/web-auth/cose-lib/src/Key/OkpKey.php', 'Cose\\Key\\RsaKey' => $vendorDir . '/web-auth/cose-lib/src/Key/RsaKey.php', 'Cose\\Key\\SymmetricKey' => $vendorDir . '/web-auth/cose-lib/src/Key/SymmetricKey.php', - 'Doctrine\\Common\\Cache\\Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php', - 'Doctrine\\Common\\Cache\\CacheProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php', - 'Doctrine\\Common\\Cache\\ClearableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php', - 'Doctrine\\Common\\Cache\\FlushableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php', - 'Doctrine\\Common\\Cache\\MultiDeleteCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php', - 'Doctrine\\Common\\Cache\\MultiGetCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php', - 'Doctrine\\Common\\Cache\\MultiOperationCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiOperationCache.php', - 'Doctrine\\Common\\Cache\\MultiPutCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php', - 'Doctrine\\Common\\Cache\\Psr6\\CacheAdapter' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php', - 'Doctrine\\Common\\Cache\\Psr6\\CacheItem' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php', - 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', - 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', - 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', - 'Doctrine\\Common\\EventArgs' => $vendorDir . '/doctrine/event-manager/src/EventArgs.php', - 'Doctrine\\Common\\EventManager' => $vendorDir . '/doctrine/event-manager/src/EventManager.php', - 'Doctrine\\Common\\EventSubscriber' => $vendorDir . '/doctrine/event-manager/src/EventSubscriber.php', 'Doctrine\\Common\\Lexer\\AbstractLexer' => $vendorDir . '/doctrine/lexer/src/AbstractLexer.php', 'Doctrine\\Common\\Lexer\\Token' => $vendorDir . '/doctrine/lexer/src/Token.php', 'Doctrine\\DBAL\\ArrayParameterType' => $vendorDir . '/doctrine/dbal/src/ArrayParameterType.php', @@ -1235,11 +1219,14 @@ 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingPositionalParameter' => $vendorDir . '/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php', 'Doctrine\\DBAL\\Cache\\ArrayResult' => $vendorDir . '/doctrine/dbal/src/Cache/ArrayResult.php', 'Doctrine\\DBAL\\Cache\\CacheException' => $vendorDir . '/doctrine/dbal/src/Cache/CacheException.php', + 'Doctrine\\DBAL\\Cache\\Exception\\NoCacheKey' => $vendorDir . '/doctrine/dbal/src/Cache/Exception/NoCacheKey.php', + 'Doctrine\\DBAL\\Cache\\Exception\\NoResultDriverConfigured' => $vendorDir . '/doctrine/dbal/src/Cache/Exception/NoResultDriverConfigured.php', 'Doctrine\\DBAL\\Cache\\QueryCacheProfile' => $vendorDir . '/doctrine/dbal/src/Cache/QueryCacheProfile.php', 'Doctrine\\DBAL\\ColumnCase' => $vendorDir . '/doctrine/dbal/src/ColumnCase.php', 'Doctrine\\DBAL\\Configuration' => $vendorDir . '/doctrine/dbal/src/Configuration.php', 'Doctrine\\DBAL\\Connection' => $vendorDir . '/doctrine/dbal/src/Connection.php', 'Doctrine\\DBAL\\ConnectionException' => $vendorDir . '/doctrine/dbal/src/ConnectionException.php', + 'Doctrine\\DBAL\\Connection\\StaticServerVersionProvider' => $vendorDir . '/doctrine/dbal/src/Connection/StaticServerVersionProvider.php', 'Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection' => $vendorDir . '/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php', 'Doctrine\\DBAL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver.php', 'Doctrine\\DBAL\\DriverManager' => $vendorDir . '/doctrine/dbal/src/DriverManager.php', @@ -1250,7 +1237,6 @@ 'Doctrine\\DBAL\\Driver\\API\\PostgreSQL\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLSrv\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLite\\ExceptionConverter' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php', - 'Doctrine\\DBAL\\Driver\\API\\SQLite\\UserDefinedFunctions' => $vendorDir . '/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php', 'Doctrine\\DBAL\\Driver\\AbstractDB2Driver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractDB2Driver.php', 'Doctrine\\DBAL\\Driver\\AbstractException' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractException.php', 'Doctrine\\DBAL\\Driver\\AbstractMySQLDriver' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractMySQLDriver.php', @@ -1263,7 +1249,8 @@ 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver\\Middleware\\EnableForeignKeys' => $vendorDir . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php', 'Doctrine\\DBAL\\Driver\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/Connection.php', 'Doctrine\\DBAL\\Driver\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/Exception.php', - 'Doctrine\\DBAL\\Driver\\Exception\\UnknownParameterType' => $vendorDir . '/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php', + 'Doctrine\\DBAL\\Driver\\Exception\\IdentityColumnsNotSupported' => $vendorDir . '/doctrine/dbal/src/Driver/Exception/IdentityColumnsNotSupported.php', + 'Doctrine\\DBAL\\Driver\\Exception\\NoIdentityValue' => $vendorDir . '/doctrine/dbal/src/Driver/Exception/NoIdentityValue.php', 'Doctrine\\DBAL\\Driver\\FetchUtils' => $vendorDir . '/doctrine/dbal/src/Driver/FetchUtils.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/Connection.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\DataSourceName' => $vendorDir . '/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php', @@ -1305,7 +1292,6 @@ 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\Error' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/Error.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\InvalidConfiguration' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/InvalidConfiguration.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\NonTerminatedStringLiteral' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php', - 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\SequenceDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\UnknownParameterIndex' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php', 'Doctrine\\DBAL\\Driver\\OCI8\\ExecutionMode' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Middleware\\InitializeSession' => $vendorDir . '/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php', @@ -1315,8 +1301,6 @@ 'Doctrine\\DBAL\\Driver\\PDO\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Exception.php', 'Doctrine\\DBAL\\Driver\\PDO\\MySQL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\OCI\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/OCI/Driver.php', - 'Doctrine\\DBAL\\Driver\\PDO\\PDOException' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/PDOException.php', - 'Doctrine\\DBAL\\Driver\\PDO\\ParameterTypeMap' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php', 'Doctrine\\DBAL\\Driver\\PDO\\PgSQL\\Driver' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/Result.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Connection' => $vendorDir . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php', @@ -1343,29 +1327,9 @@ 'Doctrine\\DBAL\\Driver\\SQLite3\\Exception' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Exception.php', 'Doctrine\\DBAL\\Driver\\SQLite3\\Result' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Result.php', 'Doctrine\\DBAL\\Driver\\SQLite3\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/SQLite3/Statement.php', - 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => $vendorDir . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', 'Doctrine\\DBAL\\Driver\\Statement' => $vendorDir . '/doctrine/dbal/src/Driver/Statement.php', - 'Doctrine\\DBAL\\Event\\ConnectionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/ConnectionEventArgs.php', - 'Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php', - 'Doctrine\\DBAL\\Event\\Listeners\\SQLSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php', - 'Doctrine\\DBAL\\Event\\Listeners\\SQLiteSessionInit' => $vendorDir . '/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableAddColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableChangeColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableRemoveColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableRenameColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaColumnDefinitionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaCreateTableColumnEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaCreateTableEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaDropTableEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaIndexDefinitionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionBeginEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionBeginEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionCommitEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionCommitEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionRollBackEventArgs' => $vendorDir . '/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php', - 'Doctrine\\DBAL\\Events' => $vendorDir . '/doctrine/dbal/src/Events.php', 'Doctrine\\DBAL\\Exception' => $vendorDir . '/doctrine/dbal/src/Exception.php', + 'Doctrine\\DBAL\\Exception\\CommitFailedRollbackOnly' => $vendorDir . '/doctrine/dbal/src/Exception/CommitFailedRollbackOnly.php', 'Doctrine\\DBAL\\Exception\\ConnectionException' => $vendorDir . '/doctrine/dbal/src/Exception/ConnectionException.php', 'Doctrine\\DBAL\\Exception\\ConnectionLost' => $vendorDir . '/doctrine/dbal/src/Exception/ConnectionLost.php', 'Doctrine\\DBAL\\Exception\\ConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/ConstraintViolationException.php', @@ -1375,78 +1339,77 @@ 'Doctrine\\DBAL\\Exception\\DatabaseRequired' => $vendorDir . '/doctrine/dbal/src/Exception/DatabaseRequired.php', 'Doctrine\\DBAL\\Exception\\DeadlockException' => $vendorDir . '/doctrine/dbal/src/Exception/DeadlockException.php', 'Doctrine\\DBAL\\Exception\\DriverException' => $vendorDir . '/doctrine/dbal/src/Exception/DriverException.php', + 'Doctrine\\DBAL\\Exception\\DriverRequired' => $vendorDir . '/doctrine/dbal/src/Exception/DriverRequired.php', 'Doctrine\\DBAL\\Exception\\ForeignKeyConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\InvalidArgumentException' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidArgumentException.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnDeclaration' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidColumnDeclaration.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidColumnType.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnLengthRequired' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnLengthRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnPrecisionRequired' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnPrecisionRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnScaleRequired' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnScaleRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidDriverClass' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidDriverClass.php', 'Doctrine\\DBAL\\Exception\\InvalidFieldNameException' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidFieldNameException.php', - 'Doctrine\\DBAL\\Exception\\InvalidLockMode' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidLockMode.php', + 'Doctrine\\DBAL\\Exception\\InvalidWrapperClass' => $vendorDir . '/doctrine/dbal/src/Exception/InvalidWrapperClass.php', 'Doctrine\\DBAL\\Exception\\LockWaitTimeoutException' => $vendorDir . '/doctrine/dbal/src/Exception/LockWaitTimeoutException.php', 'Doctrine\\DBAL\\Exception\\MalformedDsnException' => $vendorDir . '/doctrine/dbal/src/Exception/MalformedDsnException.php', + 'Doctrine\\DBAL\\Exception\\NoActiveTransaction' => $vendorDir . '/doctrine/dbal/src/Exception/NoActiveTransaction.php', 'Doctrine\\DBAL\\Exception\\NoKeyValue' => $vendorDir . '/doctrine/dbal/src/Exception/NoKeyValue.php', 'Doctrine\\DBAL\\Exception\\NonUniqueFieldNameException' => $vendorDir . '/doctrine/dbal/src/Exception/NonUniqueFieldNameException.php', 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\ReadOnlyException' => $vendorDir . '/doctrine/dbal/src/Exception/ReadOnlyException.php', 'Doctrine\\DBAL\\Exception\\RetryableException' => $vendorDir . '/doctrine/dbal/src/Exception/RetryableException.php', + 'Doctrine\\DBAL\\Exception\\SavepointsNotSupported' => $vendorDir . '/doctrine/dbal/src/Exception/SavepointsNotSupported.php', 'Doctrine\\DBAL\\Exception\\SchemaDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Exception/SchemaDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\ServerException' => $vendorDir . '/doctrine/dbal/src/Exception/ServerException.php', 'Doctrine\\DBAL\\Exception\\SyntaxErrorException' => $vendorDir . '/doctrine/dbal/src/Exception/SyntaxErrorException.php', 'Doctrine\\DBAL\\Exception\\TableExistsException' => $vendorDir . '/doctrine/dbal/src/Exception/TableExistsException.php', 'Doctrine\\DBAL\\Exception\\TableNotFoundException' => $vendorDir . '/doctrine/dbal/src/Exception/TableNotFoundException.php', 'Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException' => $vendorDir . '/doctrine/dbal/src/Exception/UniqueConstraintViolationException.php', + 'Doctrine\\DBAL\\Exception\\UnknownDriver' => $vendorDir . '/doctrine/dbal/src/Exception/UnknownDriver.php', 'Doctrine\\DBAL\\ExpandArrayParameters' => $vendorDir . '/doctrine/dbal/src/ExpandArrayParameters.php', - 'Doctrine\\DBAL\\FetchMode' => $vendorDir . '/doctrine/dbal/src/FetchMode.php', - 'Doctrine\\DBAL\\Id\\TableGenerator' => $vendorDir . '/doctrine/dbal/src/Id/TableGenerator.php', - 'Doctrine\\DBAL\\Id\\TableGeneratorSchemaVisitor' => $vendorDir . '/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php', 'Doctrine\\DBAL\\LockMode' => $vendorDir . '/doctrine/dbal/src/LockMode.php', 'Doctrine\\DBAL\\Logging\\Connection' => $vendorDir . '/doctrine/dbal/src/Logging/Connection.php', - 'Doctrine\\DBAL\\Logging\\DebugStack' => $vendorDir . '/doctrine/dbal/src/Logging/DebugStack.php', 'Doctrine\\DBAL\\Logging\\Driver' => $vendorDir . '/doctrine/dbal/src/Logging/Driver.php', - 'Doctrine\\DBAL\\Logging\\LoggerChain' => $vendorDir . '/doctrine/dbal/src/Logging/LoggerChain.php', 'Doctrine\\DBAL\\Logging\\Middleware' => $vendorDir . '/doctrine/dbal/src/Logging/Middleware.php', - 'Doctrine\\DBAL\\Logging\\SQLLogger' => $vendorDir . '/doctrine/dbal/src/Logging/SQLLogger.php', 'Doctrine\\DBAL\\Logging\\Statement' => $vendorDir . '/doctrine/dbal/src/Logging/Statement.php', 'Doctrine\\DBAL\\ParameterType' => $vendorDir . '/doctrine/dbal/src/ParameterType.php', 'Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/AbstractPlatform.php', - 'Doctrine\\DBAL\\Platforms\\DB2111Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/DB2111Platform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DateIntervalUnit' => $vendorDir . '/doctrine/dbal/src/Platforms/DateIntervalUnit.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\InvalidPlatformVersion' => $vendorDir . '/doctrine/dbal/src/Platforms/Exception/InvalidPlatformVersion.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\NoColumnsSpecifiedForTable' => $vendorDir . '/doctrine/dbal/src/Platforms/Exception/NoColumnsSpecifiedForTable.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\NotSupported' => $vendorDir . '/doctrine/dbal/src/Platforms/Exception/NotSupported.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\PlatformException' => $vendorDir . '/doctrine/dbal/src/Platforms/Exception/PlatformException.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\DB2Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\KeywordList' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/KeywordList.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDBKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDb102Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL57Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL80Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQLKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\OracleKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL100Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL94Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQLKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\ReservedKeywordsValidator' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServer2012Keywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServerKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLiteKeywords' => $vendorDir . '/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php', + 'Doctrine\\DBAL\\Platforms\\MariaDB1052Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDB1052Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDB1060Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDB1060Platform.php', 'Doctrine\\DBAL\\Platforms\\MariaDBPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDBPlatform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1027Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1027Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1043Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1043Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1052Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1052Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1060Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MariaDb1060Platform.php', - 'Doctrine\\DBAL\\Platforms\\MySQL57Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL57Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL80Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL80Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQLPlatform.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider\\CachingCharsetMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/CachingCharsetMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider\\ConnectionCharsetMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/ConnectionCharsetMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\CachingCollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\ConnectionCollationMetadataProvider' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/Comparator.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\DefaultTableOptions' => $vendorDir . '/doctrine/dbal/src/Platforms/MySQL/DefaultTableOptions.php', 'Doctrine\\DBAL\\Platforms\\OraclePlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/OraclePlatform.php', - 'Doctrine\\DBAL\\Platforms\\PostgreSQL100Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php', - 'Doctrine\\DBAL\\Platforms\\PostgreSQL94Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php', - 'Doctrine\\DBAL\\Platforms\\SQLServer2012Platform' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServer2012Platform.php', 'Doctrine\\DBAL\\Platforms\\SQLServerPlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServerPlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLServer\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServer/Comparator.php', 'Doctrine\\DBAL\\Platforms\\SQLServer\\SQL\\Builder\\SQLServerSelectSQLBuilder' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php', + 'Doctrine\\DBAL\\Platforms\\SQLitePlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLitePlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLite\\Comparator' => $vendorDir . '/doctrine/dbal/src/Platforms/SQLite/Comparator.php', - 'Doctrine\\DBAL\\Platforms\\SqlitePlatform' => $vendorDir . '/doctrine/dbal/src/Platforms/SqlitePlatform.php', 'Doctrine\\DBAL\\Platforms\\TrimMode' => $vendorDir . '/doctrine/dbal/src/Platforms/TrimMode.php', 'Doctrine\\DBAL\\Portability\\Connection' => $vendorDir . '/doctrine/dbal/src/Portability/Connection.php', 'Doctrine\\DBAL\\Portability\\Converter' => $vendorDir . '/doctrine/dbal/src/Portability/Converter.php', @@ -1456,13 +1419,18 @@ 'Doctrine\\DBAL\\Portability\\Result' => $vendorDir . '/doctrine/dbal/src/Portability/Result.php', 'Doctrine\\DBAL\\Portability\\Statement' => $vendorDir . '/doctrine/dbal/src/Portability/Statement.php', 'Doctrine\\DBAL\\Query' => $vendorDir . '/doctrine/dbal/src/Query.php', + 'Doctrine\\DBAL\\Query\\Exception\\NonUniqueAlias' => $vendorDir . '/doctrine/dbal/src/Query/Exception/NonUniqueAlias.php', + 'Doctrine\\DBAL\\Query\\Exception\\UnknownAlias' => $vendorDir . '/doctrine/dbal/src/Query/Exception/UnknownAlias.php', 'Doctrine\\DBAL\\Query\\Expression\\CompositeExpression' => $vendorDir . '/doctrine/dbal/src/Query/Expression/CompositeExpression.php', 'Doctrine\\DBAL\\Query\\Expression\\ExpressionBuilder' => $vendorDir . '/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php', 'Doctrine\\DBAL\\Query\\ForUpdate' => $vendorDir . '/doctrine/dbal/src/Query/ForUpdate.php', 'Doctrine\\DBAL\\Query\\ForUpdate\\ConflictResolutionMode' => $vendorDir . '/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php', + 'Doctrine\\DBAL\\Query\\From' => $vendorDir . '/doctrine/dbal/src/Query/From.php', + 'Doctrine\\DBAL\\Query\\Join' => $vendorDir . '/doctrine/dbal/src/Query/Join.php', 'Doctrine\\DBAL\\Query\\Limit' => $vendorDir . '/doctrine/dbal/src/Query/Limit.php', 'Doctrine\\DBAL\\Query\\QueryBuilder' => $vendorDir . '/doctrine/dbal/src/Query/QueryBuilder.php', 'Doctrine\\DBAL\\Query\\QueryException' => $vendorDir . '/doctrine/dbal/src/Query/QueryException.php', + 'Doctrine\\DBAL\\Query\\QueryType' => $vendorDir . '/doctrine/dbal/src/Query/QueryType.php', 'Doctrine\\DBAL\\Query\\SelectQuery' => $vendorDir . '/doctrine/dbal/src/Query/SelectQuery.php', 'Doctrine\\DBAL\\Result' => $vendorDir . '/doctrine/dbal/src/Result.php', 'Doctrine\\DBAL\\SQL\\Builder\\CreateSchemaObjectsSQLBuilder' => $vendorDir . '/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php', @@ -1478,7 +1446,6 @@ 'Doctrine\\DBAL\\Schema\\Column' => $vendorDir . '/doctrine/dbal/src/Schema/Column.php', 'Doctrine\\DBAL\\Schema\\ColumnDiff' => $vendorDir . '/doctrine/dbal/src/Schema/ColumnDiff.php', 'Doctrine\\DBAL\\Schema\\Comparator' => $vendorDir . '/doctrine/dbal/src/Schema/Comparator.php', - 'Doctrine\\DBAL\\Schema\\Constraint' => $vendorDir . '/doctrine/dbal/src/Schema/Constraint.php', 'Doctrine\\DBAL\\Schema\\DB2SchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/DB2SchemaManager.php', 'Doctrine\\DBAL\\Schema\\DefaultSchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Exception\\ColumnAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php', @@ -1488,7 +1455,6 @@ 'Doctrine\\DBAL\\Schema\\Exception\\IndexDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php', 'Doctrine\\DBAL\\Schema\\Exception\\IndexNameInvalid' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php', 'Doctrine\\DBAL\\Schema\\Exception\\InvalidTableName' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/InvalidTableName.php', - 'Doctrine\\DBAL\\Schema\\Exception\\NamedForeignKeyRequired' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php', 'Doctrine\\DBAL\\Schema\\Exception\\NamespaceAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php', 'Doctrine\\DBAL\\Schema\\Exception\\SequenceAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php', 'Doctrine\\DBAL\\Schema\\Exception\\SequenceDoesNotExist' => $vendorDir . '/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php', @@ -1499,40 +1465,29 @@ 'Doctrine\\DBAL\\Schema\\ForeignKeyConstraint' => $vendorDir . '/doctrine/dbal/src/Schema/ForeignKeyConstraint.php', 'Doctrine\\DBAL\\Schema\\Identifier' => $vendorDir . '/doctrine/dbal/src/Schema/Identifier.php', 'Doctrine\\DBAL\\Schema\\Index' => $vendorDir . '/doctrine/dbal/src/Schema/Index.php', - 'Doctrine\\DBAL\\Schema\\LegacySchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\MySQLSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/MySQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\OracleSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/OracleSchemaManager.php', 'Doctrine\\DBAL\\Schema\\PostgreSQLSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\SQLServerSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/SQLServerSchemaManager.php', + 'Doctrine\\DBAL\\Schema\\SQLiteSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/SQLiteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Schema' => $vendorDir . '/doctrine/dbal/src/Schema/Schema.php', 'Doctrine\\DBAL\\Schema\\SchemaConfig' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaConfig.php', 'Doctrine\\DBAL\\Schema\\SchemaDiff' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaDiff.php', 'Doctrine\\DBAL\\Schema\\SchemaException' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaException.php', 'Doctrine\\DBAL\\Schema\\SchemaManagerFactory' => $vendorDir . '/doctrine/dbal/src/Schema/SchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Sequence' => $vendorDir . '/doctrine/dbal/src/Schema/Sequence.php', - 'Doctrine\\DBAL\\Schema\\SqliteSchemaManager' => $vendorDir . '/doctrine/dbal/src/Schema/SqliteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Table' => $vendorDir . '/doctrine/dbal/src/Schema/Table.php', 'Doctrine\\DBAL\\Schema\\TableDiff' => $vendorDir . '/doctrine/dbal/src/Schema/TableDiff.php', 'Doctrine\\DBAL\\Schema\\UniqueConstraint' => $vendorDir . '/doctrine/dbal/src/Schema/UniqueConstraint.php', 'Doctrine\\DBAL\\Schema\\View' => $vendorDir . '/doctrine/dbal/src/Schema/View.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\AbstractVisitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\CreateSchemaSqlCollector' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\DropSchemaSqlCollector' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\Graphviz' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/Graphviz.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\NamespaceVisitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\RemoveNamespacedAssets' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\Visitor' => $vendorDir . '/doctrine/dbal/src/Schema/Visitor/Visitor.php', + 'Doctrine\\DBAL\\ServerVersionProvider' => $vendorDir . '/doctrine/dbal/src/ServerVersionProvider.php', 'Doctrine\\DBAL\\Statement' => $vendorDir . '/doctrine/dbal/src/Statement.php', - 'Doctrine\\DBAL\\Tools\\Console\\Command\\CommandCompatibility' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php', - 'Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\RunSqlCommand' => $vendorDir . '/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionNotFound' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider\\SingleConnectionProvider' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php', - 'Doctrine\\DBAL\\Tools\\Console\\ConsoleRunner' => $vendorDir . '/doctrine/dbal/src/Tools/Console/ConsoleRunner.php', 'Doctrine\\DBAL\\Tools\\DsnParser' => $vendorDir . '/doctrine/dbal/src/Tools/DsnParser.php', 'Doctrine\\DBAL\\TransactionIsolationLevel' => $vendorDir . '/doctrine/dbal/src/TransactionIsolationLevel.php', - 'Doctrine\\DBAL\\Types\\ArrayType' => $vendorDir . '/doctrine/dbal/src/Types/ArrayType.php', 'Doctrine\\DBAL\\Types\\AsciiStringType' => $vendorDir . '/doctrine/dbal/src/Types/AsciiStringType.php', 'Doctrine\\DBAL\\Types\\BigIntType' => $vendorDir . '/doctrine/dbal/src/Types/BigIntType.php', 'Doctrine\\DBAL\\Types\\BinaryType' => $vendorDir . '/doctrine/dbal/src/Types/BinaryType.php', @@ -1547,13 +1502,24 @@ 'Doctrine\\DBAL\\Types\\DateTimeTzType' => $vendorDir . '/doctrine/dbal/src/Types/DateTimeTzType.php', 'Doctrine\\DBAL\\Types\\DateType' => $vendorDir . '/doctrine/dbal/src/Types/DateType.php', 'Doctrine\\DBAL\\Types\\DecimalType' => $vendorDir . '/doctrine/dbal/src/Types/DecimalType.php', + 'Doctrine\\DBAL\\Types\\Exception\\InvalidFormat' => $vendorDir . '/doctrine/dbal/src/Types/Exception/InvalidFormat.php', + 'Doctrine\\DBAL\\Types\\Exception\\InvalidType' => $vendorDir . '/doctrine/dbal/src/Types/Exception/InvalidType.php', + 'Doctrine\\DBAL\\Types\\Exception\\SerializationFailed' => $vendorDir . '/doctrine/dbal/src/Types/Exception/SerializationFailed.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeAlreadyRegistered' => $vendorDir . '/doctrine/dbal/src/Types/Exception/TypeAlreadyRegistered.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeNotFound' => $vendorDir . '/doctrine/dbal/src/Types/Exception/TypeNotFound.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeNotRegistered' => $vendorDir . '/doctrine/dbal/src/Types/Exception/TypeNotRegistered.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypesAlreadyExists' => $vendorDir . '/doctrine/dbal/src/Types/Exception/TypesAlreadyExists.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypesException' => $vendorDir . '/doctrine/dbal/src/Types/Exception/TypesException.php', + 'Doctrine\\DBAL\\Types\\Exception\\UnknownColumnType' => $vendorDir . '/doctrine/dbal/src/Types/Exception/UnknownColumnType.php', + 'Doctrine\\DBAL\\Types\\Exception\\ValueNotConvertible' => $vendorDir . '/doctrine/dbal/src/Types/Exception/ValueNotConvertible.php', 'Doctrine\\DBAL\\Types\\FloatType' => $vendorDir . '/doctrine/dbal/src/Types/FloatType.php', 'Doctrine\\DBAL\\Types\\GuidType' => $vendorDir . '/doctrine/dbal/src/Types/GuidType.php', 'Doctrine\\DBAL\\Types\\IntegerType' => $vendorDir . '/doctrine/dbal/src/Types/IntegerType.php', 'Doctrine\\DBAL\\Types\\JsonType' => $vendorDir . '/doctrine/dbal/src/Types/JsonType.php', - 'Doctrine\\DBAL\\Types\\ObjectType' => $vendorDir . '/doctrine/dbal/src/Types/ObjectType.php', + 'Doctrine\\DBAL\\Types\\PhpDateMappingType' => $vendorDir . '/doctrine/dbal/src/Types/PhpDateMappingType.php', 'Doctrine\\DBAL\\Types\\PhpDateTimeMappingType' => $vendorDir . '/doctrine/dbal/src/Types/PhpDateTimeMappingType.php', 'Doctrine\\DBAL\\Types\\PhpIntegerMappingType' => $vendorDir . '/doctrine/dbal/src/Types/PhpIntegerMappingType.php', + 'Doctrine\\DBAL\\Types\\PhpTimeMappingType' => $vendorDir . '/doctrine/dbal/src/Types/PhpTimeMappingType.php', 'Doctrine\\DBAL\\Types\\SimpleArrayType' => $vendorDir . '/doctrine/dbal/src/Types/SimpleArrayType.php', 'Doctrine\\DBAL\\Types\\SmallIntType' => $vendorDir . '/doctrine/dbal/src/Types/SmallIntType.php', 'Doctrine\\DBAL\\Types\\StringType' => $vendorDir . '/doctrine/dbal/src/Types/StringType.php', @@ -1565,7 +1531,6 @@ 'Doctrine\\DBAL\\Types\\Types' => $vendorDir . '/doctrine/dbal/src/Types/Types.php', 'Doctrine\\DBAL\\Types\\VarDateTimeImmutableType' => $vendorDir . '/doctrine/dbal/src/Types/VarDateTimeImmutableType.php', 'Doctrine\\DBAL\\Types\\VarDateTimeType' => $vendorDir . '/doctrine/dbal/src/Types/VarDateTimeType.php', - 'Doctrine\\DBAL\\VersionAwarePlatformDriver' => $vendorDir . '/doctrine/dbal/src/VersionAwarePlatformDriver.php', 'Doctrine\\Deprecations\\Deprecation' => $vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php', 'Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => $vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', 'Egulias\\EmailValidator\\EmailLexer' => $vendorDir . '/egulias/email-validator/src/EmailLexer.php', diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php index a9e39f853..8164ab64b 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -82,8 +82,6 @@ 'Doctrine\\Deprecations\\' => array($vendorDir . '/doctrine/deprecations/lib/Doctrine/Deprecations'), 'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/src'), 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'), - 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), - 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/event-manager/src'), 'Cose\\' => array($vendorDir . '/web-auth/cose-lib/src'), 'CBOR\\' => array($vendorDir . '/spomky-labs/cbor-php/src'), 'Brick\\Math\\' => array($vendorDir . '/brick/math/src'), diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 914532b1a..9a5620978 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -175,8 +175,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\Deprecations\\' => 22, 'Doctrine\\DBAL\\' => 14, 'Doctrine\\Common\\Lexer\\' => 22, - 'Doctrine\\Common\\Cache\\' => 22, - 'Doctrine\\Common\\' => 16, ), 'C' => array ( @@ -499,14 +497,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 array ( 0 => __DIR__ . '/..' . '/doctrine/lexer/src', ), - 'Doctrine\\Common\\Cache\\' => - array ( - 0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache', - ), - 'Doctrine\\Common\\' => - array ( - 0 => __DIR__ . '/..' . '/doctrine/event-manager/src', - ), 'Cose\\' => array ( 0 => __DIR__ . '/..' . '/web-auth/cose-lib/src', @@ -1755,22 +1745,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Cose\\Key\\OkpKey' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Key/OkpKey.php', 'Cose\\Key\\RsaKey' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Key/RsaKey.php', 'Cose\\Key\\SymmetricKey' => __DIR__ . '/..' . '/web-auth/cose-lib/src/Key/SymmetricKey.php', - 'Doctrine\\Common\\Cache\\Cache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php', - 'Doctrine\\Common\\Cache\\CacheProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php', - 'Doctrine\\Common\\Cache\\ClearableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php', - 'Doctrine\\Common\\Cache\\FlushableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php', - 'Doctrine\\Common\\Cache\\MultiDeleteCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php', - 'Doctrine\\Common\\Cache\\MultiGetCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php', - 'Doctrine\\Common\\Cache\\MultiOperationCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiOperationCache.php', - 'Doctrine\\Common\\Cache\\MultiPutCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php', - 'Doctrine\\Common\\Cache\\Psr6\\CacheAdapter' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php', - 'Doctrine\\Common\\Cache\\Psr6\\CacheItem' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php', - 'Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php', - 'Doctrine\\Common\\Cache\\Psr6\\InvalidArgument' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php', - 'Doctrine\\Common\\Cache\\Psr6\\TypedCacheItem' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.php', - 'Doctrine\\Common\\EventArgs' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventArgs.php', - 'Doctrine\\Common\\EventManager' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventManager.php', - 'Doctrine\\Common\\EventSubscriber' => __DIR__ . '/..' . '/doctrine/event-manager/src/EventSubscriber.php', 'Doctrine\\Common\\Lexer\\AbstractLexer' => __DIR__ . '/..' . '/doctrine/lexer/src/AbstractLexer.php', 'Doctrine\\Common\\Lexer\\Token' => __DIR__ . '/..' . '/doctrine/lexer/src/Token.php', 'Doctrine\\DBAL\\ArrayParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameterType.php', @@ -1779,11 +1753,14 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\ArrayParameters\\Exception\\MissingPositionalParameter' => __DIR__ . '/..' . '/doctrine/dbal/src/ArrayParameters/Exception/MissingPositionalParameter.php', 'Doctrine\\DBAL\\Cache\\ArrayResult' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/ArrayResult.php', 'Doctrine\\DBAL\\Cache\\CacheException' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/CacheException.php', + 'Doctrine\\DBAL\\Cache\\Exception\\NoCacheKey' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/Exception/NoCacheKey.php', + 'Doctrine\\DBAL\\Cache\\Exception\\NoResultDriverConfigured' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/Exception/NoResultDriverConfigured.php', 'Doctrine\\DBAL\\Cache\\QueryCacheProfile' => __DIR__ . '/..' . '/doctrine/dbal/src/Cache/QueryCacheProfile.php', 'Doctrine\\DBAL\\ColumnCase' => __DIR__ . '/..' . '/doctrine/dbal/src/ColumnCase.php', 'Doctrine\\DBAL\\Configuration' => __DIR__ . '/..' . '/doctrine/dbal/src/Configuration.php', 'Doctrine\\DBAL\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Connection.php', 'Doctrine\\DBAL\\ConnectionException' => __DIR__ . '/..' . '/doctrine/dbal/src/ConnectionException.php', + 'Doctrine\\DBAL\\Connection\\StaticServerVersionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Connection/StaticServerVersionProvider.php', 'Doctrine\\DBAL\\Connections\\PrimaryReadReplicaConnection' => __DIR__ . '/..' . '/doctrine/dbal/src/Connections/PrimaryReadReplicaConnection.php', 'Doctrine\\DBAL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver.php', 'Doctrine\\DBAL\\DriverManager' => __DIR__ . '/..' . '/doctrine/dbal/src/DriverManager.php', @@ -1794,7 +1771,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\API\\PostgreSQL\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLSrv\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php', 'Doctrine\\DBAL\\Driver\\API\\SQLite\\ExceptionConverter' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php', - 'Doctrine\\DBAL\\Driver\\API\\SQLite\\UserDefinedFunctions' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php', 'Doctrine\\DBAL\\Driver\\AbstractDB2Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractDB2Driver.php', 'Doctrine\\DBAL\\Driver\\AbstractException' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractException.php', 'Doctrine\\DBAL\\Driver\\AbstractMySQLDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractMySQLDriver.php', @@ -1807,7 +1783,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\AbstractSQLiteDriver\\Middleware\\EnableForeignKeys' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.php', 'Doctrine\\DBAL\\Driver\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Connection.php', 'Doctrine\\DBAL\\Driver\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception.php', - 'Doctrine\\DBAL\\Driver\\Exception\\UnknownParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception/UnknownParameterType.php', + 'Doctrine\\DBAL\\Driver\\Exception\\IdentityColumnsNotSupported' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception/IdentityColumnsNotSupported.php', + 'Doctrine\\DBAL\\Driver\\Exception\\NoIdentityValue' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Exception/NoIdentityValue.php', 'Doctrine\\DBAL\\Driver\\FetchUtils' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/FetchUtils.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/Connection.php', 'Doctrine\\DBAL\\Driver\\IBMDB2\\DataSourceName' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php', @@ -1849,7 +1826,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\Error' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/Error.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\InvalidConfiguration' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/InvalidConfiguration.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\NonTerminatedStringLiteral' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.php', - 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\SequenceDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Exception\\UnknownParameterIndex' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Exception/UnknownParameterIndex.php', 'Doctrine\\DBAL\\Driver\\OCI8\\ExecutionMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/ExecutionMode.php', 'Doctrine\\DBAL\\Driver\\OCI8\\Middleware\\InitializeSession' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/OCI8/Middleware/InitializeSession.php', @@ -1859,8 +1835,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\PDO\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Exception.php', 'Doctrine\\DBAL\\Driver\\PDO\\MySQL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\OCI\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/OCI/Driver.php', - 'Doctrine\\DBAL\\Driver\\PDO\\PDOException' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/PDOException.php', - 'Doctrine\\DBAL\\Driver\\PDO\\ParameterTypeMap' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php', 'Doctrine\\DBAL\\Driver\\PDO\\PgSQL\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php', 'Doctrine\\DBAL\\Driver\\PDO\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/Result.php', 'Doctrine\\DBAL\\Driver\\PDO\\SQLSrv\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php', @@ -1887,29 +1861,9 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Driver\\SQLite3\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Exception.php', 'Doctrine\\DBAL\\Driver\\SQLite3\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Result.php', 'Doctrine\\DBAL\\Driver\\SQLite3\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/SQLite3/Statement.php', - 'Doctrine\\DBAL\\Driver\\ServerInfoAwareConnection' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php', 'Doctrine\\DBAL\\Driver\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Driver/Statement.php', - 'Doctrine\\DBAL\\Event\\ConnectionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/ConnectionEventArgs.php', - 'Doctrine\\DBAL\\Event\\Listeners\\OracleSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php', - 'Doctrine\\DBAL\\Event\\Listeners\\SQLSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php', - 'Doctrine\\DBAL\\Event\\Listeners\\SQLiteSessionInit' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableAddColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableChangeColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableRemoveColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaAlterTableRenameColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaColumnDefinitionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaCreateTableColumnEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaCreateTableEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaDropTableEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaEventArgs.php', - 'Doctrine\\DBAL\\Event\\SchemaIndexDefinitionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionBeginEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionBeginEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionCommitEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionCommitEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionEventArgs.php', - 'Doctrine\\DBAL\\Event\\TransactionRollBackEventArgs' => __DIR__ . '/..' . '/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php', - 'Doctrine\\DBAL\\Events' => __DIR__ . '/..' . '/doctrine/dbal/src/Events.php', 'Doctrine\\DBAL\\Exception' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception.php', + 'Doctrine\\DBAL\\Exception\\CommitFailedRollbackOnly' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/CommitFailedRollbackOnly.php', 'Doctrine\\DBAL\\Exception\\ConnectionException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConnectionException.php', 'Doctrine\\DBAL\\Exception\\ConnectionLost' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConnectionLost.php', 'Doctrine\\DBAL\\Exception\\ConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ConstraintViolationException.php', @@ -1919,78 +1873,77 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Exception\\DatabaseRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DatabaseRequired.php', 'Doctrine\\DBAL\\Exception\\DeadlockException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DeadlockException.php', 'Doctrine\\DBAL\\Exception\\DriverException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DriverException.php', + 'Doctrine\\DBAL\\Exception\\DriverRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/DriverRequired.php', 'Doctrine\\DBAL\\Exception\\ForeignKeyConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ForeignKeyConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidArgumentException.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnDeclaration' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidColumnDeclaration.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidColumnType.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnLengthRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnLengthRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnPrecisionRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnPrecisionRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidColumnType\\ColumnScaleRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidColumnType/ColumnScaleRequired.php', + 'Doctrine\\DBAL\\Exception\\InvalidDriverClass' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidDriverClass.php', 'Doctrine\\DBAL\\Exception\\InvalidFieldNameException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidFieldNameException.php', - 'Doctrine\\DBAL\\Exception\\InvalidLockMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidLockMode.php', + 'Doctrine\\DBAL\\Exception\\InvalidWrapperClass' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/InvalidWrapperClass.php', 'Doctrine\\DBAL\\Exception\\LockWaitTimeoutException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/LockWaitTimeoutException.php', 'Doctrine\\DBAL\\Exception\\MalformedDsnException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/MalformedDsnException.php', + 'Doctrine\\DBAL\\Exception\\NoActiveTransaction' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NoActiveTransaction.php', 'Doctrine\\DBAL\\Exception\\NoKeyValue' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NoKeyValue.php', 'Doctrine\\DBAL\\Exception\\NonUniqueFieldNameException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NonUniqueFieldNameException.php', 'Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/NotNullConstraintViolationException.php', 'Doctrine\\DBAL\\Exception\\ReadOnlyException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ReadOnlyException.php', 'Doctrine\\DBAL\\Exception\\RetryableException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/RetryableException.php', + 'Doctrine\\DBAL\\Exception\\SavepointsNotSupported' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/SavepointsNotSupported.php', 'Doctrine\\DBAL\\Exception\\SchemaDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/SchemaDoesNotExist.php', 'Doctrine\\DBAL\\Exception\\ServerException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/ServerException.php', 'Doctrine\\DBAL\\Exception\\SyntaxErrorException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/SyntaxErrorException.php', 'Doctrine\\DBAL\\Exception\\TableExistsException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/TableExistsException.php', 'Doctrine\\DBAL\\Exception\\TableNotFoundException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/TableNotFoundException.php', 'Doctrine\\DBAL\\Exception\\UniqueConstraintViolationException' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/UniqueConstraintViolationException.php', + 'Doctrine\\DBAL\\Exception\\UnknownDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/Exception/UnknownDriver.php', 'Doctrine\\DBAL\\ExpandArrayParameters' => __DIR__ . '/..' . '/doctrine/dbal/src/ExpandArrayParameters.php', - 'Doctrine\\DBAL\\FetchMode' => __DIR__ . '/..' . '/doctrine/dbal/src/FetchMode.php', - 'Doctrine\\DBAL\\Id\\TableGenerator' => __DIR__ . '/..' . '/doctrine/dbal/src/Id/TableGenerator.php', - 'Doctrine\\DBAL\\Id\\TableGeneratorSchemaVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php', 'Doctrine\\DBAL\\LockMode' => __DIR__ . '/..' . '/doctrine/dbal/src/LockMode.php', 'Doctrine\\DBAL\\Logging\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Connection.php', - 'Doctrine\\DBAL\\Logging\\DebugStack' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/DebugStack.php', 'Doctrine\\DBAL\\Logging\\Driver' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Driver.php', - 'Doctrine\\DBAL\\Logging\\LoggerChain' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/LoggerChain.php', 'Doctrine\\DBAL\\Logging\\Middleware' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Middleware.php', - 'Doctrine\\DBAL\\Logging\\SQLLogger' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/SQLLogger.php', 'Doctrine\\DBAL\\Logging\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Logging/Statement.php', 'Doctrine\\DBAL\\ParameterType' => __DIR__ . '/..' . '/doctrine/dbal/src/ParameterType.php', 'Doctrine\\DBAL\\Platforms\\AbstractMySQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/AbstractMySQLPlatform.php', 'Doctrine\\DBAL\\Platforms\\AbstractPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/AbstractPlatform.php', - 'Doctrine\\DBAL\\Platforms\\DB2111Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DB2111Platform.php', 'Doctrine\\DBAL\\Platforms\\DB2Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DB2Platform.php', 'Doctrine\\DBAL\\Platforms\\DateIntervalUnit' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/DateIntervalUnit.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\InvalidPlatformVersion' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Exception/InvalidPlatformVersion.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\NoColumnsSpecifiedForTable' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Exception/NoColumnsSpecifiedForTable.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\NotSupported' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Exception/NotSupported.php', + 'Doctrine\\DBAL\\Platforms\\Exception\\PlatformException' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Exception/PlatformException.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\DB2Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/DB2Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\KeywordList' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/KeywordList.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDBKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\MariaDb102Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MariaDb102Keywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL57Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MySQL57Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQL80Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MySQL80Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\MySQLKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/MySQLKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\OracleKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/OracleKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL100Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL100Keywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQL94Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQL94Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\PostgreSQLKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/PostgreSQLKeywords.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\ReservedKeywordsValidator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/ReservedKeywordsValidator.php', - 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServer2012Keywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLServerKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLServerKeywords.php', 'Doctrine\\DBAL\\Platforms\\Keywords\\SQLiteKeywords' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/Keywords/SQLiteKeywords.php', + 'Doctrine\\DBAL\\Platforms\\MariaDB1052Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDB1052Platform.php', + 'Doctrine\\DBAL\\Platforms\\MariaDB1060Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDB1060Platform.php', 'Doctrine\\DBAL\\Platforms\\MariaDBPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDBPlatform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1027Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1027Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1043Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1043Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1052Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1052Platform.php', - 'Doctrine\\DBAL\\Platforms\\MariaDb1060Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MariaDb1060Platform.php', - 'Doctrine\\DBAL\\Platforms\\MySQL57Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL57Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQL80Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL80Platform.php', 'Doctrine\\DBAL\\Platforms\\MySQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQLPlatform.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider\\CachingCharsetMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/CachingCharsetMetadataProvider.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\CharsetMetadataProvider\\ConnectionCharsetMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/ConnectionCharsetMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\CachingCollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\CollationMetadataProvider\\ConnectionCollationMetadataProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php', 'Doctrine\\DBAL\\Platforms\\MySQL\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/Comparator.php', + 'Doctrine\\DBAL\\Platforms\\MySQL\\DefaultTableOptions' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/MySQL/DefaultTableOptions.php', 'Doctrine\\DBAL\\Platforms\\OraclePlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/OraclePlatform.php', - 'Doctrine\\DBAL\\Platforms\\PostgreSQL100Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php', - 'Doctrine\\DBAL\\Platforms\\PostgreSQL94Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQL94Platform.php', 'Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/PostgreSQLPlatform.php', - 'Doctrine\\DBAL\\Platforms\\SQLServer2012Platform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServer2012Platform.php', 'Doctrine\\DBAL\\Platforms\\SQLServerPlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServerPlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLServer\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServer/Comparator.php', 'Doctrine\\DBAL\\Platforms\\SQLServer\\SQL\\Builder\\SQLServerSelectSQLBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php', + 'Doctrine\\DBAL\\Platforms\\SQLitePlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLitePlatform.php', 'Doctrine\\DBAL\\Platforms\\SQLite\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SQLite/Comparator.php', - 'Doctrine\\DBAL\\Platforms\\SqlitePlatform' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/SqlitePlatform.php', 'Doctrine\\DBAL\\Platforms\\TrimMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Platforms/TrimMode.php', 'Doctrine\\DBAL\\Portability\\Connection' => __DIR__ . '/..' . '/doctrine/dbal/src/Portability/Connection.php', 'Doctrine\\DBAL\\Portability\\Converter' => __DIR__ . '/..' . '/doctrine/dbal/src/Portability/Converter.php', @@ -2000,13 +1953,18 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Portability\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Portability/Result.php', 'Doctrine\\DBAL\\Portability\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Portability/Statement.php', 'Doctrine\\DBAL\\Query' => __DIR__ . '/..' . '/doctrine/dbal/src/Query.php', + 'Doctrine\\DBAL\\Query\\Exception\\NonUniqueAlias' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Exception/NonUniqueAlias.php', + 'Doctrine\\DBAL\\Query\\Exception\\UnknownAlias' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Exception/UnknownAlias.php', 'Doctrine\\DBAL\\Query\\Expression\\CompositeExpression' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Expression/CompositeExpression.php', 'Doctrine\\DBAL\\Query\\Expression\\ExpressionBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php', 'Doctrine\\DBAL\\Query\\ForUpdate' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/ForUpdate.php', 'Doctrine\\DBAL\\Query\\ForUpdate\\ConflictResolutionMode' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php', + 'Doctrine\\DBAL\\Query\\From' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/From.php', + 'Doctrine\\DBAL\\Query\\Join' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Join.php', 'Doctrine\\DBAL\\Query\\Limit' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/Limit.php', 'Doctrine\\DBAL\\Query\\QueryBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/QueryBuilder.php', 'Doctrine\\DBAL\\Query\\QueryException' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/QueryException.php', + 'Doctrine\\DBAL\\Query\\QueryType' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/QueryType.php', 'Doctrine\\DBAL\\Query\\SelectQuery' => __DIR__ . '/..' . '/doctrine/dbal/src/Query/SelectQuery.php', 'Doctrine\\DBAL\\Result' => __DIR__ . '/..' . '/doctrine/dbal/src/Result.php', 'Doctrine\\DBAL\\SQL\\Builder\\CreateSchemaObjectsSQLBuilder' => __DIR__ . '/..' . '/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php', @@ -2022,7 +1980,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\Column' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Column.php', 'Doctrine\\DBAL\\Schema\\ColumnDiff' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/ColumnDiff.php', 'Doctrine\\DBAL\\Schema\\Comparator' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Comparator.php', - 'Doctrine\\DBAL\\Schema\\Constraint' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Constraint.php', 'Doctrine\\DBAL\\Schema\\DB2SchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/DB2SchemaManager.php', 'Doctrine\\DBAL\\Schema\\DefaultSchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Exception\\ColumnAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php', @@ -2032,7 +1989,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\Exception\\IndexDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php', 'Doctrine\\DBAL\\Schema\\Exception\\IndexNameInvalid' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php', 'Doctrine\\DBAL\\Schema\\Exception\\InvalidTableName' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/InvalidTableName.php', - 'Doctrine\\DBAL\\Schema\\Exception\\NamedForeignKeyRequired' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php', 'Doctrine\\DBAL\\Schema\\Exception\\NamespaceAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php', 'Doctrine\\DBAL\\Schema\\Exception\\SequenceAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php', 'Doctrine\\DBAL\\Schema\\Exception\\SequenceDoesNotExist' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php', @@ -2043,40 +1999,29 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Schema\\ForeignKeyConstraint' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/ForeignKeyConstraint.php', 'Doctrine\\DBAL\\Schema\\Identifier' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Identifier.php', 'Doctrine\\DBAL\\Schema\\Index' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Index.php', - 'Doctrine\\DBAL\\Schema\\LegacySchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\MySQLSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/MySQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\OracleSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/OracleSchemaManager.php', 'Doctrine\\DBAL\\Schema\\PostgreSQLSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php', 'Doctrine\\DBAL\\Schema\\SQLServerSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SQLServerSchemaManager.php', + 'Doctrine\\DBAL\\Schema\\SQLiteSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SQLiteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Schema' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Schema.php', 'Doctrine\\DBAL\\Schema\\SchemaConfig' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaConfig.php', 'Doctrine\\DBAL\\Schema\\SchemaDiff' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaDiff.php', 'Doctrine\\DBAL\\Schema\\SchemaException' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaException.php', 'Doctrine\\DBAL\\Schema\\SchemaManagerFactory' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SchemaManagerFactory.php', 'Doctrine\\DBAL\\Schema\\Sequence' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Sequence.php', - 'Doctrine\\DBAL\\Schema\\SqliteSchemaManager' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/SqliteSchemaManager.php', 'Doctrine\\DBAL\\Schema\\Table' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Table.php', 'Doctrine\\DBAL\\Schema\\TableDiff' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/TableDiff.php', 'Doctrine\\DBAL\\Schema\\UniqueConstraint' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/UniqueConstraint.php', 'Doctrine\\DBAL\\Schema\\View' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/View.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\AbstractVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\CreateSchemaSqlCollector' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/CreateSchemaSqlCollector.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\DropSchemaSqlCollector' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\Graphviz' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/Graphviz.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\NamespaceVisitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\RemoveNamespacedAssets' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/RemoveNamespacedAssets.php', - 'Doctrine\\DBAL\\Schema\\Visitor\\Visitor' => __DIR__ . '/..' . '/doctrine/dbal/src/Schema/Visitor/Visitor.php', + 'Doctrine\\DBAL\\ServerVersionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/ServerVersionProvider.php', 'Doctrine\\DBAL\\Statement' => __DIR__ . '/..' . '/doctrine/dbal/src/Statement.php', - 'Doctrine\\DBAL\\Tools\\Console\\Command\\CommandCompatibility' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php', - 'Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\Command\\RunSqlCommand' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionNotFound' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionProvider.php', 'Doctrine\\DBAL\\Tools\\Console\\ConnectionProvider\\SingleConnectionProvider' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConnectionProvider/SingleConnectionProvider.php', - 'Doctrine\\DBAL\\Tools\\Console\\ConsoleRunner' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/Console/ConsoleRunner.php', 'Doctrine\\DBAL\\Tools\\DsnParser' => __DIR__ . '/..' . '/doctrine/dbal/src/Tools/DsnParser.php', 'Doctrine\\DBAL\\TransactionIsolationLevel' => __DIR__ . '/..' . '/doctrine/dbal/src/TransactionIsolationLevel.php', - 'Doctrine\\DBAL\\Types\\ArrayType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/ArrayType.php', 'Doctrine\\DBAL\\Types\\AsciiStringType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/AsciiStringType.php', 'Doctrine\\DBAL\\Types\\BigIntType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/BigIntType.php', 'Doctrine\\DBAL\\Types\\BinaryType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/BinaryType.php', @@ -2091,13 +2036,24 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Types\\DateTimeTzType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/DateTimeTzType.php', 'Doctrine\\DBAL\\Types\\DateType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/DateType.php', 'Doctrine\\DBAL\\Types\\DecimalType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/DecimalType.php', + 'Doctrine\\DBAL\\Types\\Exception\\InvalidFormat' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/InvalidFormat.php', + 'Doctrine\\DBAL\\Types\\Exception\\InvalidType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/InvalidType.php', + 'Doctrine\\DBAL\\Types\\Exception\\SerializationFailed' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/SerializationFailed.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeAlreadyRegistered' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/TypeAlreadyRegistered.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeNotFound' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/TypeNotFound.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypeNotRegistered' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/TypeNotRegistered.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypesAlreadyExists' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/TypesAlreadyExists.php', + 'Doctrine\\DBAL\\Types\\Exception\\TypesException' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/TypesException.php', + 'Doctrine\\DBAL\\Types\\Exception\\UnknownColumnType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/UnknownColumnType.php', + 'Doctrine\\DBAL\\Types\\Exception\\ValueNotConvertible' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Exception/ValueNotConvertible.php', 'Doctrine\\DBAL\\Types\\FloatType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/FloatType.php', 'Doctrine\\DBAL\\Types\\GuidType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/GuidType.php', 'Doctrine\\DBAL\\Types\\IntegerType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/IntegerType.php', 'Doctrine\\DBAL\\Types\\JsonType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/JsonType.php', - 'Doctrine\\DBAL\\Types\\ObjectType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/ObjectType.php', + 'Doctrine\\DBAL\\Types\\PhpDateMappingType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/PhpDateMappingType.php', 'Doctrine\\DBAL\\Types\\PhpDateTimeMappingType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/PhpDateTimeMappingType.php', 'Doctrine\\DBAL\\Types\\PhpIntegerMappingType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/PhpIntegerMappingType.php', + 'Doctrine\\DBAL\\Types\\PhpTimeMappingType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/PhpTimeMappingType.php', 'Doctrine\\DBAL\\Types\\SimpleArrayType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/SimpleArrayType.php', 'Doctrine\\DBAL\\Types\\SmallIntType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/SmallIntType.php', 'Doctrine\\DBAL\\Types\\StringType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/StringType.php', @@ -2109,7 +2065,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Doctrine\\DBAL\\Types\\Types' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/Types.php', 'Doctrine\\DBAL\\Types\\VarDateTimeImmutableType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/VarDateTimeImmutableType.php', 'Doctrine\\DBAL\\Types\\VarDateTimeType' => __DIR__ . '/..' . '/doctrine/dbal/src/Types/VarDateTimeType.php', - 'Doctrine\\DBAL\\VersionAwarePlatformDriver' => __DIR__ . '/..' . '/doctrine/dbal/src/VersionAwarePlatformDriver.php', 'Doctrine\\Deprecations\\Deprecation' => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations/Deprecation.php', 'Doctrine\\Deprecations\\PHPUnit\\VerifyDeprecations' => __DIR__ . '/..' . '/doctrine/deprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.php', 'Egulias\\EmailValidator\\EmailLexer' => __DIR__ . '/..' . '/egulias/email-validator/src/EmailLexer.php', diff --git a/composer/installed.json b/composer/installed.json index a4347165d..4c5597799 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -445,147 +445,46 @@ }, "install-path": "../deepdiver1975/tarstreamer" }, - { - "name": "doctrine/cache", - "version": "2.2.0", - "version_normalized": "2.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", - "shasum": "" - }, - "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" - }, - "time": "2022-05-20T20:07:39+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", - "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", - "php", - "redis", - "xcache" - ], - "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "install-path": "../doctrine/cache" - }, { "name": "doctrine/dbal", - "version": "3.8.3", - "version_normalized": "3.8.3.0", + "version": "4.0.4", + "version_normalized": "4.0.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c" + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c", - "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/50fda19f80724b55ff770bb4ff352407008e63c5", + "reference": "50fda19f80724b55ff770bb4ff352407008e63c5", "shasum": "" }, "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", + "php": "^8.1", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.58", - "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.16", - "psalm/plugin-phpunit": "0.18.4", + "jetbrains/phpstorm-stubs": "2023.2", + "phpstan/phpstan": "1.11.5", + "phpstan/phpstan-phpunit": "1.4.0", + "phpstan/phpstan-strict-rules": "^1.6", + "phpunit/phpunit": "10.5.22", + "psalm/plugin-phpunit": "0.19.0", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.9.0", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0", - "vimeo/psalm": "4.30.0" + "squizlabs/php_codesniffer": "3.10.1", + "symfony/cache": "^6.3.8|^7.0", + "symfony/console": "^5.4|^6.3|^7.0", + "vimeo/psalm": "5.24.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "time": "2024-03-03T15:55:06+00:00", - "bin": [ - "bin/doctrine-dbal" - ], + "time": "2024-06-19T11:57:23+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -639,7 +538,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.3" + "source": "https://github.com/doctrine/dbal/tree/4.0.4" }, "funding": [ { @@ -707,101 +606,6 @@ }, "install-path": "../doctrine/deprecations" }, - { - "name": "doctrine/event-manager", - "version": "1.2.0", - "version_normalized": "1.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.24" - }, - "time": "2022-10-12T20:51:15+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "install-path": "../doctrine/event-manager" - }, { "name": "doctrine/lexer", "version": "3.0.1", diff --git a/composer/installed.php b/composer/installed.php index 34cb232d9..bf689aef3 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => 'nextcloud/3rdparty', - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'reference' => null, + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => '6084b912d75b9e1e466b5211169e9e1c165587c5', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), @@ -73,19 +73,10 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'doctrine/cache' => array( - 'pretty_version' => '2.2.0', - 'version' => '2.2.0.0', - 'reference' => '1ca8f21980e770095a31456042471a57bc4c68fb', - 'type' => 'library', - 'install_path' => __DIR__ . '/../doctrine/cache', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'doctrine/dbal' => array( - 'pretty_version' => '3.8.3', - 'version' => '3.8.3.0', - 'reference' => 'db922ba9436b7b18a23d1653a0b41ff2369ca41c', + 'pretty_version' => '4.0.4', + 'version' => '4.0.4.0', + 'reference' => '50fda19f80724b55ff770bb4ff352407008e63c5', 'type' => 'library', 'install_path' => __DIR__ . '/../doctrine/dbal', 'aliases' => array(), @@ -100,15 +91,6 @@ 'aliases' => array(), 'dev_requirement' => false, ), - 'doctrine/event-manager' => array( - 'pretty_version' => '1.2.0', - 'version' => '1.2.0.0', - 'reference' => '95aa4cb529f1e96576f3fda9f5705ada4056a520', - 'type' => 'library', - 'install_path' => __DIR__ . '/../doctrine/event-manager', - 'aliases' => array(), - 'dev_requirement' => false, - ), 'doctrine/lexer' => array( 'pretty_version' => '3.0.1', 'version' => '3.0.1.0', @@ -290,9 +272,9 @@ 'dev_requirement' => false, ), 'nextcloud/3rdparty' => array( - 'pretty_version' => '1.0.0+no-version-set', - 'version' => '1.0.0.0', - 'reference' => null, + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'reference' => '6084b912d75b9e1e466b5211169e9e1c165587c5', 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), diff --git a/doctrine/cache/LICENSE b/doctrine/cache/LICENSE deleted file mode 100644 index 8c38cc1bc..000000000 --- a/doctrine/cache/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2006-2015 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php b/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php deleted file mode 100644 index 4cfab6c0f..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php +++ /dev/null @@ -1,90 +0,0 @@ -hits - * Number of keys that have been requested and found present. - * - * - misses - * Number of items that have been requested and not found. - * - * - uptime - * Time that the server is running. - * - * - memory_usage - * Memory used by this server to store items. - * - * - memory_available - * Memory allowed to use for storage. - * - * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. - */ - public function getStats(); -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php deleted file mode 100644 index 180482a7b..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php +++ /dev/null @@ -1,325 +0,0 @@ -namespace = (string) $namespace; - $this->namespaceVersion = null; - } - - /** - * Retrieves the namespace that prefixes all cache ids. - * - * @return string - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * {@inheritdoc} - */ - public function fetch($id) - { - return $this->doFetch($this->getNamespacedId($id)); - } - - /** - * {@inheritdoc} - */ - public function fetchMultiple(array $keys) - { - if (empty($keys)) { - return []; - } - - // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys - $namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys)); - $items = $this->doFetchMultiple($namespacedKeys); - $foundItems = []; - - // no internal array function supports this sort of mapping: needs to be iterative - // this filters and combines keys in one pass - foreach ($namespacedKeys as $requestedKey => $namespacedKey) { - if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) { - continue; - } - - $foundItems[$requestedKey] = $items[$namespacedKey]; - } - - return $foundItems; - } - - /** - * {@inheritdoc} - */ - public function saveMultiple(array $keysAndValues, $lifetime = 0) - { - $namespacedKeysAndValues = []; - foreach ($keysAndValues as $key => $value) { - $namespacedKeysAndValues[$this->getNamespacedId($key)] = $value; - } - - return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime); - } - - /** - * {@inheritdoc} - */ - public function contains($id) - { - return $this->doContains($this->getNamespacedId($id)); - } - - /** - * {@inheritdoc} - */ - public function save($id, $data, $lifeTime = 0) - { - return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); - } - - /** - * {@inheritdoc} - */ - public function deleteMultiple(array $keys) - { - return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys)); - } - - /** - * {@inheritdoc} - */ - public function delete($id) - { - return $this->doDelete($this->getNamespacedId($id)); - } - - /** - * {@inheritdoc} - */ - public function getStats() - { - return $this->doGetStats(); - } - - /** - * {@inheritDoc} - */ - public function flushAll() - { - return $this->doFlush(); - } - - /** - * {@inheritDoc} - */ - public function deleteAll() - { - $namespaceCacheKey = $this->getNamespaceCacheKey(); - $namespaceVersion = $this->getNamespaceVersion() + 1; - - if ($this->doSave($namespaceCacheKey, $namespaceVersion)) { - $this->namespaceVersion = $namespaceVersion; - - return true; - } - - return false; - } - - /** - * Prefixes the passed id with the configured namespace value. - * - * @param string $id The id to namespace. - * - * @return string The namespaced id. - */ - private function getNamespacedId(string $id): string - { - $namespaceVersion = $this->getNamespaceVersion(); - - return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); - } - - /** - * Returns the namespace cache key. - */ - private function getNamespaceCacheKey(): string - { - return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); - } - - /** - * Returns the namespace version. - */ - private function getNamespaceVersion(): int - { - if ($this->namespaceVersion !== null) { - return $this->namespaceVersion; - } - - $namespaceCacheKey = $this->getNamespaceCacheKey(); - $this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1; - - return $this->namespaceVersion; - } - - /** - * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. - * - * @param string[] $keys Array of keys to retrieve from cache - * - * @return mixed[] Array of values retrieved for the given keys. - */ - protected function doFetchMultiple(array $keys) - { - $returnValues = []; - - foreach ($keys as $key) { - $item = $this->doFetch($key); - if ($item === false && ! $this->doContains($key)) { - continue; - } - - $returnValues[$key] = $item; - } - - return $returnValues; - } - - /** - * Fetches an entry from the cache. - * - * @param string $id The id of the cache entry to fetch. - * - * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id. - */ - abstract protected function doFetch($id); - - /** - * Tests if an entry exists in the cache. - * - * @param string $id The cache id of the entry to check for. - * - * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. - */ - abstract protected function doContains($id); - - /** - * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it. - * - * @param mixed[] $keysAndValues Array of keys and values to save in cache - * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these - * cache entries (0 => infinite lifeTime). - * - * @return bool TRUE if the operation was successful, FALSE if it wasn't. - */ - protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) - { - $success = true; - - foreach ($keysAndValues as $key => $value) { - if ($this->doSave($key, $value, $lifetime)) { - continue; - } - - $success = false; - } - - return $success; - } - - /** - * Puts data into the cache. - * - * @param string $id The cache id. - * @param string $data The cache entry/data. - * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this - * cache entry (0 => infinite lifeTime). - * - * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. - */ - abstract protected function doSave($id, $data, $lifeTime = 0); - - /** - * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it. - * - * @param string[] $keys Array of keys to delete from cache - * - * @return bool TRUE if the operation was successful, FALSE if it wasn't - */ - protected function doDeleteMultiple(array $keys) - { - $success = true; - - foreach ($keys as $key) { - if ($this->doDelete($key)) { - continue; - } - - $success = false; - } - - return $success; - } - - /** - * Deletes a cache entry. - * - * @param string $id The cache id. - * - * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. - */ - abstract protected function doDelete($id); - - /** - * Flushes all cache entries. - * - * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. - */ - abstract protected function doFlush(); - - /** - * Retrieves cached information from the data store. - * - * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. - */ - abstract protected function doGetStats(); -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php b/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php deleted file mode 100644 index b94618e46..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php +++ /dev/null @@ -1,21 +0,0 @@ - infinite lifeTime). - * - * @return bool TRUE if the operation was successful, FALSE if it wasn't. - */ - public function saveMultiple(array $keysAndValues, $lifetime = 0); -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php deleted file mode 100644 index d3693b7c6..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php +++ /dev/null @@ -1,340 +0,0 @@ - */ - private $deferredItems = []; - - public static function wrap(Cache $cache): CacheItemPoolInterface - { - if ($cache instanceof DoctrineProvider && ! $cache->getNamespace()) { - return $cache->getPool(); - } - - if ($cache instanceof SymfonyDoctrineProvider && ! $cache->getNamespace()) { - $getPool = function () { - // phpcs:ignore Squiz.Scope.StaticThisUsage.Found - return $this->pool; - }; - - return $getPool->bindTo($cache, SymfonyDoctrineProvider::class)(); - } - - return new self($cache); - } - - private function __construct(Cache $cache) - { - $this->cache = $cache; - } - - /** @internal */ - public function getCache(): Cache - { - return $this->cache; - } - - /** - * {@inheritDoc} - */ - public function getItem($key): CacheItemInterface - { - assert(self::validKey($key)); - - if (isset($this->deferredItems[$key])) { - $this->commit(); - } - - $value = $this->cache->fetch($key); - - if (PHP_VERSION_ID >= 80000) { - if ($value !== false) { - return new TypedCacheItem($key, $value, true); - } - - return new TypedCacheItem($key, null, false); - } - - if ($value !== false) { - return new CacheItem($key, $value, true); - } - - return new CacheItem($key, null, false); - } - - /** - * {@inheritDoc} - */ - public function getItems(array $keys = []): array - { - if ($this->deferredItems) { - $this->commit(); - } - - assert(self::validKeys($keys)); - - $values = $this->doFetchMultiple($keys); - $items = []; - - if (PHP_VERSION_ID >= 80000) { - foreach ($keys as $key) { - if (array_key_exists($key, $values)) { - $items[$key] = new TypedCacheItem($key, $values[$key], true); - } else { - $items[$key] = new TypedCacheItem($key, null, false); - } - } - - return $items; - } - - foreach ($keys as $key) { - if (array_key_exists($key, $values)) { - $items[$key] = new CacheItem($key, $values[$key], true); - } else { - $items[$key] = new CacheItem($key, null, false); - } - } - - return $items; - } - - /** - * {@inheritDoc} - */ - public function hasItem($key): bool - { - assert(self::validKey($key)); - - if (isset($this->deferredItems[$key])) { - $this->commit(); - } - - return $this->cache->contains($key); - } - - public function clear(): bool - { - $this->deferredItems = []; - - if (! $this->cache instanceof ClearableCache) { - return false; - } - - return $this->cache->deleteAll(); - } - - /** - * {@inheritDoc} - */ - public function deleteItem($key): bool - { - assert(self::validKey($key)); - unset($this->deferredItems[$key]); - - return $this->cache->delete($key); - } - - /** - * {@inheritDoc} - */ - public function deleteItems(array $keys): bool - { - foreach ($keys as $key) { - assert(self::validKey($key)); - unset($this->deferredItems[$key]); - } - - return $this->doDeleteMultiple($keys); - } - - public function save(CacheItemInterface $item): bool - { - return $this->saveDeferred($item) && $this->commit(); - } - - public function saveDeferred(CacheItemInterface $item): bool - { - if (! $item instanceof CacheItem && ! $item instanceof TypedCacheItem) { - return false; - } - - $this->deferredItems[$item->getKey()] = $item; - - return true; - } - - public function commit(): bool - { - if (! $this->deferredItems) { - return true; - } - - $now = microtime(true); - $itemsCount = 0; - $byLifetime = []; - $expiredKeys = []; - - foreach ($this->deferredItems as $key => $item) { - $lifetime = ($item->getExpiry() ?? $now) - $now; - - if ($lifetime < 0) { - $expiredKeys[] = $key; - - continue; - } - - ++$itemsCount; - $byLifetime[(int) $lifetime][$key] = $item->get(); - } - - $this->deferredItems = []; - - switch (count($expiredKeys)) { - case 0: - break; - case 1: - $this->cache->delete(current($expiredKeys)); - break; - default: - $this->doDeleteMultiple($expiredKeys); - break; - } - - if ($itemsCount === 1) { - return $this->cache->save($key, $item->get(), (int) $lifetime); - } - - $success = true; - foreach ($byLifetime as $lifetime => $values) { - $success = $this->doSaveMultiple($values, $lifetime) && $success; - } - - return $success; - } - - public function __destruct() - { - $this->commit(); - } - - /** - * @param mixed $key - */ - private static function validKey($key): bool - { - if (! is_string($key)) { - throw new InvalidArgument(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key))); - } - - if ($key === '') { - throw new InvalidArgument('Cache key length must be greater than zero.'); - } - - if (strpbrk($key, self::RESERVED_CHARACTERS) !== false) { - throw new InvalidArgument(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS)); - } - - return true; - } - - /** - * @param mixed[] $keys - */ - private static function validKeys(array $keys): bool - { - foreach ($keys as $key) { - self::validKey($key); - } - - return true; - } - - /** - * @param mixed[] $keys - */ - private function doDeleteMultiple(array $keys): bool - { - if ($this->cache instanceof MultiDeleteCache) { - return $this->cache->deleteMultiple($keys); - } - - $success = true; - foreach ($keys as $key) { - $success = $this->cache->delete($key) && $success; - } - - return $success; - } - - /** - * @param mixed[] $keys - * - * @return mixed[] - */ - private function doFetchMultiple(array $keys): array - { - if ($this->cache instanceof MultiGetCache) { - return $this->cache->fetchMultiple($keys); - } - - $values = []; - foreach ($keys as $key) { - $value = $this->cache->fetch($key); - if (! $value) { - continue; - } - - $values[$key] = $value; - } - - return $values; - } - - /** - * @param mixed[] $keysAndValues - */ - private function doSaveMultiple(array $keysAndValues, int $lifetime = 0): bool - { - if ($this->cache instanceof MultiPutCache) { - return $this->cache->saveMultiple($keysAndValues, $lifetime); - } - - $success = true; - foreach ($keysAndValues as $key => $value) { - $success = $this->cache->save($key, $value, $lifetime) && $success; - } - - return $success; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php deleted file mode 100644 index 0b6f0a28d..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.php +++ /dev/null @@ -1,118 +0,0 @@ -key = $key; - $this->value = $data; - $this->isHit = $isHit; - } - - public function getKey(): string - { - return $this->key; - } - - /** - * {@inheritDoc} - * - * @return mixed - */ - public function get() - { - return $this->value; - } - - public function isHit(): bool - { - return $this->isHit; - } - - /** - * {@inheritDoc} - */ - public function set($value): self - { - $this->value = $value; - - return $this; - } - - /** - * {@inheritDoc} - */ - public function expiresAt($expiration): self - { - if ($expiration === null) { - $this->expiry = null; - } elseif ($expiration instanceof DateTimeInterface) { - $this->expiry = (float) $expiration->format('U.u'); - } else { - throw new TypeError(sprintf( - 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', - is_object($expiration) ? get_class($expiration) : gettype($expiration) - )); - } - - return $this; - } - - /** - * {@inheritDoc} - */ - public function expiresAfter($time): self - { - if ($time === null) { - $this->expiry = null; - } elseif ($time instanceof DateInterval) { - $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); - } elseif (is_int($time)) { - $this->expiry = $time + microtime(true); - } else { - throw new TypeError(sprintf( - 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', - is_object($time) ? get_class($time) : gettype($time) - )); - } - - return $this; - } - - /** - * @internal - */ - public function getExpiry(): ?float - { - return $this->expiry; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php deleted file mode 100644 index 3b0f416c1..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Doctrine\Common\Cache\Psr6; - -use Doctrine\Common\Cache\Cache; -use Doctrine\Common\Cache\CacheProvider; -use Psr\Cache\CacheItemPoolInterface; -use Symfony\Component\Cache\Adapter\DoctrineAdapter as SymfonyDoctrineAdapter; -use Symfony\Contracts\Service\ResetInterface; - -use function rawurlencode; - -/** - * This class was copied from the Symfony Framework, see the original copyright - * notice above. The code is distributed subject to the license terms in - * https://github.com/symfony/symfony/blob/ff0cf61278982539c49e467db9ab13cbd342f76d/LICENSE - */ -final class DoctrineProvider extends CacheProvider -{ - /** @var CacheItemPoolInterface */ - private $pool; - - public static function wrap(CacheItemPoolInterface $pool): Cache - { - if ($pool instanceof CacheAdapter) { - return $pool->getCache(); - } - - if ($pool instanceof SymfonyDoctrineAdapter) { - $getCache = function () { - // phpcs:ignore Squiz.Scope.StaticThisUsage.Found - return $this->provider; - }; - - return $getCache->bindTo($pool, SymfonyDoctrineAdapter::class)(); - } - - return new self($pool); - } - - private function __construct(CacheItemPoolInterface $pool) - { - $this->pool = $pool; - } - - /** @internal */ - public function getPool(): CacheItemPoolInterface - { - return $this->pool; - } - - public function reset(): void - { - if ($this->pool instanceof ResetInterface) { - $this->pool->reset(); - } - - $this->setNamespace($this->getNamespace()); - } - - /** - * {@inheritdoc} - */ - protected function doFetch($id) - { - $item = $this->pool->getItem(rawurlencode($id)); - - return $item->isHit() ? $item->get() : false; - } - - /** - * {@inheritdoc} - * - * @return bool - */ - protected function doContains($id) - { - return $this->pool->hasItem(rawurlencode($id)); - } - - /** - * {@inheritdoc} - * - * @return bool - */ - protected function doSave($id, $data, $lifeTime = 0) - { - $item = $this->pool->getItem(rawurlencode($id)); - - if (0 < $lifeTime) { - $item->expiresAfter($lifeTime); - } - - return $this->pool->save($item->set($data)); - } - - /** - * {@inheritdoc} - * - * @return bool - */ - protected function doDelete($id) - { - return $this->pool->deleteItem(rawurlencode($id)); - } - - /** - * {@inheritdoc} - * - * @return bool - */ - protected function doFlush() - { - return $this->pool->clear(); - } - - /** - * {@inheritdoc} - * - * @return array|null - */ - protected function doGetStats() - { - return null; - } -} diff --git a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php b/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php deleted file mode 100644 index 196f1bca9..000000000 --- a/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.php +++ /dev/null @@ -1,13 +0,0 @@ -key; - } - - public function get(): mixed - { - return $this->value; - } - - public function isHit(): bool - { - return $this->isHit; - } - - public function set(mixed $value): static - { - $this->value = $value; - - return $this; - } - - /** - * {@inheritDoc} - */ - public function expiresAt($expiration): static - { - if ($expiration === null) { - $this->expiry = null; - } elseif ($expiration instanceof DateTimeInterface) { - $this->expiry = (float) $expiration->format('U.u'); - } else { - throw new TypeError(sprintf( - 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', - get_debug_type($expiration) - )); - } - - return $this; - } - - /** - * {@inheritDoc} - */ - public function expiresAfter($time): static - { - if ($time === null) { - $this->expiry = null; - } elseif ($time instanceof DateInterval) { - $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); - } elseif (is_int($time)) { - $this->expiry = $time + microtime(true); - } else { - throw new TypeError(sprintf( - 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', - get_debug_type($time) - )); - } - - return $this; - } - - /** - * @internal - */ - public function getExpiry(): ?float - { - return $this->expiry; - } -} diff --git a/doctrine/dbal/src/ArrayParameterType.php b/doctrine/dbal/src/ArrayParameterType.php index 65e1a29c2..851d47d18 100644 --- a/doctrine/dbal/src/ArrayParameterType.php +++ b/doctrine/dbal/src/ArrayParameterType.php @@ -1,42 +1,39 @@ ParameterType::INTEGER, + self::STRING => ParameterType::STRING, + self::ASCII => ParameterType::ASCII, + self::BINARY => ParameterType::BINARY, + }; } } diff --git a/doctrine/dbal/src/ArrayParameters/Exception.php b/doctrine/dbal/src/ArrayParameters/Exception.php index 7fc0f7060..e5a580b76 100644 --- a/doctrine/dbal/src/ArrayParameters/Exception.php +++ b/doctrine/dbal/src/ArrayParameters/Exception.php @@ -1,5 +1,7 @@ > */ - private array $data; - - private int $columnCount = 0; - private int $num = 0; + private readonly int $columnCount; + private int $num = 0; /** @param list> $data */ - public function __construct(array $data) + public function __construct(private array $data) { - $this->data = $data; - if (count($data) === 0) { - return; - } - - $this->columnCount = count($data[0]); + $this->columnCount = $data === [] ? 0 : count($data[0]); } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { $row = $this->fetch(); @@ -43,18 +34,12 @@ public function fetchNumeric() return array_values($row); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->fetch(); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { $row = $this->fetch(); @@ -105,7 +90,7 @@ public function free(): void } /** @return array|false */ - private function fetch() + private function fetch(): array|false { if (! isset($this->data[$this->num])) { return false; diff --git a/doctrine/dbal/src/Cache/CacheException.php b/doctrine/dbal/src/Cache/CacheException.php index 18e95d6be..a6913ed0c 100644 --- a/doctrine/dbal/src/Cache/CacheException.php +++ b/doctrine/dbal/src/Cache/CacheException.php @@ -1,21 +1,12 @@ lifetime = $lifetime; - $this->cacheKey = $cacheKey; - if ($resultCache instanceof CacheItemPoolInterface) { - $this->resultCache = $resultCache; - } elseif ($resultCache instanceof Cache) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4620', - 'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.', - Cache::class, - __METHOD__, - CacheItemPoolInterface::class, - ); - - $this->resultCache = CacheAdapter::wrap($resultCache); - } elseif ($resultCache !== null) { - throw new TypeError(sprintf( - '$resultCache: Expected either null or an instance of %s or %s, got %s.', - CacheItemPoolInterface::class, - Cache::class, - get_class($resultCache), - )); - } + public function __construct( + private readonly int $lifetime = 0, + private readonly ?string $cacheKey = null, + private readonly ?CacheItemPoolInterface $resultCache = null, + ) { } public function getResultCache(): ?CacheItemPoolInterface @@ -68,38 +33,16 @@ public function getResultCache(): ?CacheItemPoolInterface return $this->resultCache; } - /** - * @deprecated Use {@see getResultCache()} instead. - * - * @return Cache|null - */ - public function getResultCacheDriver() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4620', - '%s is deprecated, call getResultCache() instead.', - __METHOD__, - ); - - return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null; - } - - /** @return int */ - public function getLifetime() + public function getLifetime(): int { return $this->lifetime; } - /** - * @return string - * - * @throws CacheException - */ - public function getCacheKey() + /** @throws CacheException */ + public function getCacheKey(): string { if ($this->cacheKey === null) { - throw CacheException::noCacheKey(); + throw NoCacheKey::new(); } return $this->cacheKey; @@ -108,14 +51,13 @@ public function getCacheKey() /** * Generates the real cache key from query, params, types and connection parameters. * - * @param string $sql - * @param list|array $params - * @param array|array $types - * @param array $connectionParams + * @param list|array $params + * @param array $connectionParams + * @psalm-param array|array $types * * @return array{string, string} */ - public function generateCacheKeys($sql, $params, $types, array $connectionParams = []) + public function generateCacheKeys(string $sql, array $params, array $types, array $connectionParams = []): array { if (isset($connectionParams['password'])) { unset($connectionParams['password']); @@ -137,39 +79,12 @@ public function setResultCache(CacheItemPoolInterface $cache): QueryCacheProfile return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); } - /** - * @deprecated Use {@see setResultCache()} instead. - * - * @return QueryCacheProfile - */ - public function setResultCacheDriver(Cache $cache) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4620', - '%s is deprecated, call setResultCache() instead.', - __METHOD__, - ); - - return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache)); - } - - /** - * @param string|null $cacheKey - * - * @return QueryCacheProfile - */ - public function setCacheKey($cacheKey) + public function setCacheKey(?string $cacheKey): self { return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache); } - /** - * @param int $lifetime - * - * @return QueryCacheProfile - */ - public function setLifetime($lifetime) + public function setLifetime(int $lifetime): self { return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCache); } diff --git a/doctrine/dbal/src/ColumnCase.php b/doctrine/dbal/src/ColumnCase.php index cb0dd409b..687a04f80 100644 --- a/doctrine/dbal/src/ColumnCase.php +++ b/doctrine/dbal/src/ColumnCase.php @@ -1,28 +1,21 @@ sqlLogger = $logger; - } - - /** - * Gets the SQL logger that is used. - * - * @deprecated - */ - public function getSQLLogger(): ?SQLLogger - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4967', - '%s is deprecated.', - __METHOD__, - ); - - return $this->sqlLogger; - } - /** * Gets the cache driver implementation that is used for query result caching. */ @@ -115,78 +51,26 @@ public function getResultCache(): ?CacheItemPoolInterface return $this->resultCache; } - /** - * Gets the cache driver implementation that is used for query result caching. - * - * @deprecated Use {@see getResultCache()} instead. - */ - public function getResultCacheImpl(): ?Cache - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4620', - '%s is deprecated, call getResultCache() instead.', - __METHOD__, - ); - - return $this->resultCacheImpl; - } - /** * Sets the cache driver implementation that is used for query result caching. */ public function setResultCache(CacheItemPoolInterface $cache): void { - $this->resultCacheImpl = DoctrineProvider::wrap($cache); - $this->resultCache = $cache; - } - - /** - * Sets the cache driver implementation that is used for query result caching. - * - * @deprecated Use {@see setResultCache()} instead. - */ - public function setResultCacheImpl(Cache $cacheImpl): void - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4620', - '%s is deprecated, call setResultCache() instead.', - __METHOD__, - ); - - $this->resultCacheImpl = $cacheImpl; - $this->resultCache = CacheAdapter::wrap($cacheImpl); + $this->resultCache = $cache; } /** * Sets the callable to use to filter schema assets. */ - public function setSchemaAssetsFilter(?callable $callable = null): void + public function setSchemaAssetsFilter(callable $schemaAssetsFilter): void { - if (func_num_args() < 1) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5483', - 'Not passing an argument to %s is deprecated.', - __METHOD__, - ); - } elseif ($callable === null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5483', - 'Using NULL as a schema asset filter is deprecated.' - . ' Use a callable that always returns true instead.', - ); - } - - $this->schemaAssetsFilter = $callable; + $this->schemaAssetsFilter = $schemaAssetsFilter; } /** * Returns the callable to use to filter schema assets. */ - public function getSchemaAssetsFilter(): ?callable + public function getSchemaAssetsFilter(): callable { return $this->schemaAssetsFilter; } @@ -198,7 +82,7 @@ public function getSchemaAssetsFilter(): ?callable * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either * the method commit or the method rollback. By default, new connections are in auto-commit mode. * - * @see getAutoCommit + * @see getAutoCommit * * @param bool $autoCommit True to enable auto-commit mode; false to disable it */ @@ -250,15 +134,22 @@ public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFacto return $this; } + /** @return true */ public function getDisableTypeComments(): bool { - return $this->disableTypeComments; + return true; } - /** @return $this */ + /** + * @param true $disableTypeComments + * + * @return $this + */ public function setDisableTypeComments(bool $disableTypeComments): self { - $this->disableTypeComments = $disableTypeComments; + if (! $disableTypeComments) { + throw new InvalidArgumentException('Column comments cannot be enabled anymore.'); + } return $this; } diff --git a/doctrine/dbal/src/Connection.php b/doctrine/dbal/src/Connection.php index 76b427b5b..184b01b3a 100644 --- a/doctrine/dbal/src/Connection.php +++ b/doctrine/dbal/src/Connection.php @@ -1,109 +1,65 @@ , WrapperParameterType>|array * @psalm-consistent-constructor */ -class Connection +class Connection implements ServerVersionProvider { - /** - * Represents an array of ints to be expanded by Doctrine SQL parsing. - * - * @deprecated Use {@see ArrayParameterType::INTEGER} instead. - */ - public const PARAM_INT_ARRAY = ArrayParameterType::INTEGER; - - /** - * Represents an array of strings to be expanded by Doctrine SQL parsing. - * - * @deprecated Use {@see ArrayParameterType::STRING} instead. - */ - public const PARAM_STR_ARRAY = ArrayParameterType::STRING; - - /** - * Represents an array of ascii strings to be expanded by Doctrine SQL parsing. - * - * @deprecated Use {@see ArrayParameterType::ASCII} instead. - */ - public const PARAM_ASCII_STR_ARRAY = ArrayParameterType::ASCII; - - /** - * Offset by which PARAM_* constants are detected as arrays of the param type. - * - * @internal Should be used only within the wrapper layer. - */ - public const ARRAY_PARAM_OFFSET = 100; - /** * The wrapped driver connection. - * - * @var DriverConnection|null */ - protected $_conn; + protected ?DriverConnection $_conn = null; - /** @var Configuration */ - protected $_config; - - /** - * @deprecated - * - * @var EventManager - */ - protected $_eventManager; - - /** - * @deprecated Use {@see createExpressionBuilder()} instead. - * - * @var ExpressionBuilder - */ - protected $_expr; + protected Configuration $_config; /** * The current auto-commit mode of this connection. @@ -117,15 +73,8 @@ class Connection /** * The currently active transaction isolation level or NULL before it has been determined. - * - * @var TransactionIsolationLevel::*|null */ - private $transactionIsolationLevel; - - /** - * If nested transactions should use savepoints. - */ - private bool $nestTransactionsWithSavepoints = false; + private ?TransactionIsolationLevel $transactionIsolationLevel = null; /** * The parameters used during creation of the Connection instance. @@ -143,22 +92,6 @@ class Connection private ?ExceptionConverter $exceptionConverter = null; private ?Parser $parser = null; - /** - * The schema manager. - * - * @deprecated Use {@see createSchemaManager()} instead. - * - * @var AbstractSchemaManager|null - */ - protected $_schemaManager; - - /** - * The used DBAL driver. - * - * @var Driver - */ - protected $_driver; - /** * Flag that indicates whether the current transaction is marked for rollback only. */ @@ -171,66 +104,23 @@ class Connection * * @internal The connection can be only instantiated by the driver manager. * - * @param array $params The connection parameters. - * @param Driver $driver The driver to use. - * @param Configuration|null $config The configuration, optional. - * @param EventManager|null $eventManager The event manager, optional. + * @param array $params The connection parameters. + * @param Driver $driver The driver to use. + * @param Configuration|null $config The configuration, optional. * @psalm-param Params $params - * - * @throws Exception */ public function __construct( #[SensitiveParameter] array $params, - Driver $driver, + protected Driver $driver, ?Configuration $config = null, - ?EventManager $eventManager = null ) { - $this->_driver = $driver; - $this->params = $params; - - // Create default config and event manager if none given - $config ??= new Configuration(); - $eventManager ??= new EventManager(); - - $this->_config = $config; - $this->_eventManager = $eventManager; - - if (isset($params['platform'])) { - if (! $params['platform'] instanceof Platforms\AbstractPlatform) { - throw Exception::invalidPlatformType($params['platform']); - } + $this->_config = $config ?? new Configuration(); + $this->params = $params; + $this->autoCommit = $this->_config->getAutoCommit(); - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5699', - 'The "platform" connection parameter is deprecated.' - . ' Use a driver middleware that would instantiate the platform instead.', - ); - - $this->platform = $params['platform']; - $this->platform->setEventManager($this->_eventManager); - $this->platform->setDisableTypeComments($config->getDisableTypeComments()); - } - - $this->_expr = $this->createExpressionBuilder(); - - $this->autoCommit = $config->getAutoCommit(); - - $schemaManagerFactory = $config->getSchemaManagerFactory(); - if ($schemaManagerFactory === null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5812', - 'Not configuring a schema manager factory is deprecated.' - . ' Use %s which is going to be the default in DBAL 4.', - DefaultSchemaManagerFactory::class, - ); - - $schemaManagerFactory = new LegacySchemaManagerFactory(); - } - - $this->schemaManagerFactory = $schemaManagerFactory; + $this->schemaManagerFactory = $this->_config->getSchemaManagerFactory() + ?? new DefaultSchemaManagerFactory(); } /** @@ -241,7 +131,7 @@ public function __construct( * @return array * @psalm-return Params */ - public function getParams() + public function getParams(): array { return $this->params; } @@ -255,7 +145,7 @@ public function getParams() * * @throws Exception */ - public function getDatabase() + public function getDatabase(): ?string { $platform = $this->getDatabasePlatform(); $query = $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression()); @@ -268,56 +158,37 @@ public function getDatabase() /** * Gets the DBAL driver instance. - * - * @return Driver */ - public function getDriver() + public function getDriver(): Driver { - return $this->_driver; + return $this->driver; } /** * Gets the Configuration used by the Connection. - * - * @return Configuration */ - public function getConfiguration() + public function getConfiguration(): Configuration { return $this->_config; } - /** - * Gets the EventManager used by the Connection. - * - * @deprecated - * - * @return EventManager - */ - public function getEventManager() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - '%s is deprecated.', - __METHOD__, - ); - - return $this->_eventManager; - } - /** * Gets the DatabasePlatform for the connection. * - * @return AbstractPlatform - * * @throws Exception */ - public function getDatabasePlatform() + public function getDatabasePlatform(): AbstractPlatform { if ($this->platform === null) { - $this->platform = $this->detectDatabasePlatform(); - $this->platform->setEventManager($this->_eventManager); - $this->platform->setDisableTypeComments($this->_config->getDisableTypeComments()); + $versionProvider = $this; + + if (isset($this->params['serverVersion'])) { + $versionProvider = new StaticServerVersionProvider($this->params['serverVersion']); + } elseif (isset($this->params['primary']['serverVersion'])) { + $versionProvider = new StaticServerVersionProvider($this->params['primary']['serverVersion']); + } + + $this->platform = $this->driver->getDatabasePlatform($versionProvider); } return $this->platform; @@ -332,50 +203,18 @@ public function createExpressionBuilder(): ExpressionBuilder } /** - * Gets the ExpressionBuilder for the connection. - * - * @deprecated Use {@see createExpressionBuilder()} instead. - * - * @return ExpressionBuilder - */ - public function getExpressionBuilder() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4515', - 'Connection::getExpressionBuilder() is deprecated,' - . ' use Connection::createExpressionBuilder() instead.', - ); - - return $this->_expr; - } - - /** - * Establishes the connection with the database. - * - * @internal This method will be made protected in DBAL 4.0. - * - * @return bool TRUE if the connection was successfully established, FALSE if - * the connection is already open. + * Establishes the connection with the database and returns the underlying connection. * * @throws Exception - * - * @psalm-assert !null $this->_conn */ - public function connect() + protected function connect(): DriverConnection { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4966', - 'Public access to Connection::connect() is deprecated.', - ); - if ($this->_conn !== null) { - return false; + return $this->_conn; } try { - $this->_conn = $this->_driver->connect($this->params); + $connection = $this->_conn = $this->driver->connect($this->params); } catch (Driver\Exception $e) { throw $this->convertException($e); } @@ -384,144 +223,17 @@ public function connect() $this->beginTransaction(); } - if ($this->_eventManager->hasListeners(Events::postConnect)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated. Implement a middleware instead.', - Events::postConnect, - ); - - $eventArgs = new Event\ConnectionEventArgs($this); - $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); - } - - return true; + return $connection; } /** - * Detects and sets the database platform. - * - * Evaluates custom platform class and version in order to set the correct platform. - * - * @throws Exception If an invalid platform was specified for this connection. - */ - private function detectDatabasePlatform(): AbstractPlatform - { - $version = $this->getDatabasePlatformVersion(); - - if ($version !== null) { - assert($this->_driver instanceof VersionAwarePlatformDriver); - - return $this->_driver->createDatabasePlatformForVersion($version); - } - - return $this->_driver->getDatabasePlatform(); - } - - /** - * Returns the version of the related platform if applicable. - * - * Returns null if either the driver is not capable to create version - * specific platform instances, no explicit server version was specified - * or the underlying driver connection cannot determine the platform - * version without having to query it (performance reasons). - * - * @return string|null - * - * @throws Throwable - */ - private function getDatabasePlatformVersion() - { - // Driver does not support version specific platforms. - if (! $this->_driver instanceof VersionAwarePlatformDriver) { - return null; - } - - // Explicit platform version requested (supersedes auto-detection). - if (isset($this->params['serverVersion'])) { - return $this->params['serverVersion']; - } - - if (isset($this->params['primary']) && isset($this->params['primary']['serverVersion'])) { - return $this->params['primary']['serverVersion']; - } - - // If not connected, we need to connect now to determine the platform version. - if ($this->_conn === null) { - try { - $this->connect(); - } catch (Exception $originalException) { - if (! isset($this->params['dbname'])) { - throw $originalException; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5707', - 'Relying on a fallback connection used to determine the database platform while connecting' - . ' to a non-existing database is deprecated. Either use an existing database name in' - . ' connection parameters or omit the database name if the platform' - . ' and the server configuration allow that.', - ); - - // The database to connect to might not yet exist. - // Retry detection without database name connection parameter. - $params = $this->params; - - unset($this->params['dbname']); - - try { - $this->connect(); - } catch (Exception $fallbackException) { - // Either the platform does not support database-less connections - // or something else went wrong. - throw $originalException; - } finally { - $this->params = $params; - } - - $serverVersion = $this->getServerVersion(); - - // Close "temporary" connection to allow connecting to the real database again. - $this->close(); - - return $serverVersion; - } - } - - return $this->getServerVersion(); - } - - /** - * Returns the database server version if the underlying driver supports it. - * - * @return string|null + * {@inheritDoc} * * @throws Exception */ - private function getServerVersion() + public function getServerVersion(): string { - $connection = $this->getWrappedConnection(); - - // Automatic platform version detection. - if ($connection instanceof ServerInfoAwareConnection) { - try { - return $connection->getServerVersion(); - } catch (Driver\Exception $e) { - throw $this->convertException($e); - } - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4750', - 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', - get_class($connection), - ); - - // Unable to detect platform version. - return null; + return $this->connect()->getServerVersion(); } /** @@ -531,9 +243,9 @@ private function getServerVersion() * * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise. */ - public function isAutoCommit() + public function isAutoCommit(): bool { - return $this->autoCommit === true; + return $this->autoCommit; } /** @@ -546,16 +258,13 @@ public function isAutoCommit() * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. * - * @see isAutoCommit + * @see isAutoCommit * - * @param bool $autoCommit True to enable auto-commit mode; false to disable it. - * - * @return void + * @throws ConnectionException + * @throws DriverException */ - public function setAutoCommit($autoCommit) + public function setAutoCommit(bool $autoCommit): void { - $autoCommit = (bool) $autoCommit; - // Mode not changed, no-op. if ($autoCommit === $this->autoCommit) { return; @@ -575,15 +284,14 @@ public function setAutoCommit($autoCommit) * Prepares and executes an SQL query and returns the first row of the result * as an associative array. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return array|false False is returned if no rows are found. * * @throws Exception */ - public function fetchAssociative(string $query, array $params = [], array $types = []) + public function fetchAssociative(string $query, array $params = [], array $types = []): array|false { return $this->executeQuery($query, $params, $types)->fetchAssociative(); } @@ -592,15 +300,14 @@ public function fetchAssociative(string $query, array $params = [], array $types * Prepares and executes an SQL query and returns the first row of the result * as a numerically indexed array. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return list|false False is returned if no rows are found. * * @throws Exception */ - public function fetchNumeric(string $query, array $params = [], array $types = []) + public function fetchNumeric(string $query, array $params = [], array $types = []): array|false { return $this->executeQuery($query, $params, $types)->fetchNumeric(); } @@ -609,25 +316,22 @@ public function fetchNumeric(string $query, array $params = [], array $types = [ * Prepares and executes an SQL query and returns the value of a single column * of the first row of the result. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return mixed|false False is returned if no rows are found. * * @throws Exception */ - public function fetchOne(string $query, array $params = [], array $types = []) + public function fetchOne(string $query, array $params = [], array $types = []): mixed { return $this->executeQuery($query, $params, $types)->fetchOne(); } /** * Whether an actual connection to the database is established. - * - * @return bool */ - public function isConnected() + public function isConnected(): bool { return $this->_conn !== null; } @@ -637,7 +341,7 @@ public function isConnected() * * @return bool TRUE if a transaction is currently active, FALSE otherwise. */ - public function isTransactionActive() + public function isTransactionActive(): bool { return $this->transactionNestingLevel > 0; } @@ -645,24 +349,17 @@ public function isTransactionActive() /** * Adds condition based on the criteria to the query components * - * @param array $criteria Map of key columns to their values - * @param string[] $columns Column names - * @param mixed[] $values Column values - * @param string[] $conditions Key conditions + * @param array $criteria Map of key columns to their values * - * @throws Exception + * @return array{list, list, list} */ - private function addCriteriaCondition( - array $criteria, - array &$columns, - array &$values, - array &$conditions - ): void { - $platform = $this->getDatabasePlatform(); + private function getCriteriaCondition(array $criteria): array + { + $columns = $values = $conditions = []; foreach ($criteria as $columnName => $value) { if ($value === null) { - $conditions[] = $platform->getIsNullExpression($columnName); + $conditions[] = $columnName . ' IS NULL'; continue; } @@ -670,6 +367,8 @@ private function addCriteriaCondition( $values[] = $value; $conditions[] = $columnName . ' = ?'; } + + return [$columns, $values, $conditions]; } /** @@ -677,26 +376,25 @@ private function addCriteriaCondition( * * Table expression and columns are not escaped and are not safe for user-input. * - * @param string $table Table name - * @param array $criteria Deletion criteria - * @param array|array $types Parameter types + * @param array $criteria + * @param array, string|ParameterType|Type>|array $types * - * @return int|string The number of affected rows. + * @return int|numeric-string The number of affected rows. * * @throws Exception */ - public function delete($table, array $criteria, array $types = []) + public function delete(string $table, array $criteria = [], array $types = []): int|string { - if (count($criteria) === 0) { - throw InvalidArgumentException::fromEmptyCriteria(); - } + [$columns, $values, $conditions] = $this->getCriteriaCondition($criteria); - $columns = $values = $conditions = []; + $sql = 'DELETE FROM ' . $table; - $this->addCriteriaCondition($criteria, $columns, $values, $conditions); + if ($conditions !== []) { + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } return $this->executeStatement( - 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), + $sql, $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types, ); @@ -704,10 +402,8 @@ public function delete($table, array $criteria, array $types = []) /** * Closes the connection. - * - * @return void */ - public function close() + public function close(): void { $this->_conn = null; $this->transactionNestingLevel = 0; @@ -716,27 +412,25 @@ public function close() /** * Sets the transaction isolation level. * - * @param TransactionIsolationLevel::* $level The level to set. - * - * @return int|string + * @param TransactionIsolationLevel $level The level to set. * * @throws Exception */ - public function setTransactionIsolation($level) + public function setTransactionIsolation(TransactionIsolationLevel $level): void { $this->transactionIsolationLevel = $level; - return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); + $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); } /** * Gets the currently active transaction isolation level. * - * @return TransactionIsolationLevel::* The current transaction isolation level. + * @return TransactionIsolationLevel The current transaction isolation level. * * @throws Exception */ - public function getTransactionIsolation() + public function getTransactionIsolation(): TransactionIsolationLevel { return $this->transactionIsolationLevel ??= $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); } @@ -746,16 +440,15 @@ public function getTransactionIsolation() * * Table expression and columns are not escaped and are not safe for user-input. * - * @param string $table Table name - * @param array $data Column-value pairs - * @param array $criteria Update criteria - * @param array|array $types Parameter types + * @param array $data + * @param array $criteria + * @param array, string|ParameterType|Type>|array $types * - * @return int|string The number of affected rows. + * @return int|numeric-string The number of affected rows. * * @throws Exception */ - public function update($table, array $data, array $criteria, array $types = []) + public function update(string $table, array $data, array $criteria = [], array $types = []): int|string { $columns = $values = $conditions = $set = []; @@ -765,14 +458,21 @@ public function update($table, array $data, array $criteria, array $types = []) $set[] = $columnName . ' = ?'; } - $this->addCriteriaCondition($criteria, $columns, $values, $conditions); + [$criteriaColumns, $criteriaValues, $criteriaConditions] = $this->getCriteriaCondition($criteria); + + $columns = array_merge($columns, $criteriaColumns); + $values = array_merge($values, $criteriaValues); + $conditions = array_merge($conditions, $criteriaConditions); if (is_string(key($types))) { $types = $this->extractTypeValues($columns, $types); } - $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) - . ' WHERE ' . implode(' AND ', $conditions); + $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set); + + if ($conditions !== []) { + $sql .= ' WHERE ' . implode(' AND ', $conditions); + } return $this->executeStatement($sql, $values, $types); } @@ -782,15 +482,14 @@ public function update($table, array $data, array $criteria, array $types = []) * * Table expression and columns are not escaped and are not safe for user-input. * - * @param string $table Table name - * @param array $data Column-value pairs - * @param array|array $types Parameter types + * @param array $data + * @param array, string|ParameterType|Type>|array $types * - * @return int|string The number of affected rows. + * @return int|numeric-string The number of affected rows. * * @throws Exception */ - public function insert($table, array $data, array $types = []) + public function insert(string $table, array $data, array $types = []): int|string { if (count($data) === 0) { return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()'); @@ -817,16 +516,16 @@ public function insert($table, array $data, array $types = []) /** * Extract ordered type list from an ordered column list and type map. * - * @param array $columnList - * @param array|array $types + * @param array $columns + * @param array|array $types * - * @return array|array + * @return array, string|ParameterType|Type> */ - private function extractTypeValues(array $columnList, array $types): array + private function extractTypeValues(array $columns, array $types): array { $typeValues = []; - foreach ($columnList as $columnName) { + foreach ($columns as $columnName) { $typeValues[] = $types[$columnName] ?? ParameterType::STRING; } @@ -843,39 +542,29 @@ private function extractTypeValues(array $columnList, array $types): array * you SHOULD use them. In general, they end up causing way more * problems than they solve. * - * @param string $str The name to be quoted. + * @param string $identifier The identifier to be quoted. * - * @return string The quoted name. + * @return string The quoted identifier. */ - public function quoteIdentifier($str) + public function quoteIdentifier(string $identifier): string { - return $this->getDatabasePlatform()->quoteIdentifier($str); + return $this->getDatabasePlatform()->quoteIdentifier($identifier); } /** * The usage of this method is discouraged. Use prepared statements * or {@see AbstractPlatform::quoteStringLiteral()} instead. - * - * @param mixed $value - * @param int|string|Type|null $type - * - * @return mixed */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - $connection = $this->getWrappedConnection(); - - [$value, $bindingType] = $this->getBindingInfo($value, $type); - - return $connection->quote($value, $bindingType); + return $this->connect()->quote($value); } /** * Prepares and executes an SQL query and returns the result as an array of numeric arrays. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return list> * @@ -889,9 +578,8 @@ public function fetchAllNumeric(string $query, array $params = [], array $types /** * Prepares and executes an SQL query and returns the result as an array of associative arrays. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return list> * @@ -906,9 +594,8 @@ public function fetchAllAssociative(string $query, array $params = [], array $ty * Prepares and executes an SQL query and returns the result as an associative array with the keys * mapped to the first column and the values mapped to the second column. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return array * @@ -924,9 +611,8 @@ public function fetchAllKeyValue(string $query, array $params = [], array $types * to the first column and the values being an associative array representing the rest of the columns * and their values. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return array> * @@ -940,9 +626,8 @@ public function fetchAllAssociativeIndexed(string $query, array $params = [], ar /** * Prepares and executes an SQL query and returns the result as an array of the first column values. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return list * @@ -956,9 +641,8 @@ public function fetchFirstColumn(string $query, array $params = [], array $types /** * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return Traversable> * @@ -973,9 +657,8 @@ public function iterateNumeric(string $query, array $params = [], array $types = * Prepares and executes an SQL query and returns the result as an iterator over rows represented * as associative arrays. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return Traversable> * @@ -990,9 +673,8 @@ public function iterateAssociative(string $query, array $params = [], array $typ * Prepares and executes an SQL query and returns the result as an iterator with the keys * mapped to the first column and the values mapped to the second column. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return Traversable * @@ -1008,9 +690,8 @@ public function iterateKeyValue(string $query, array $params = [], array $types * to the first column and the values being an associative array representing the rest of the columns * and their values. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return Traversable> * @@ -1024,9 +705,8 @@ public function iterateAssociativeIndexed(string $query, array $params = [], arr /** * Prepares and executes an SQL query and returns the result as an iterator over the first column values. * - * @param string $query SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return Traversable * @@ -1046,7 +726,7 @@ public function iterateColumn(string $query, array $params = [], array $types = */ public function prepare(string $sql): Statement { - $connection = $this->getWrappedConnection(); + $connection = $this->connect(); try { $statement = $connection->prepare($sql); @@ -1061,36 +741,27 @@ public function prepare(string $sql): Statement * Executes an, optionally parameterized, SQL query. * * If the query is parametrized, a prepared statement is used. - * If an SQLLogger is configured, the execution is logged. * - * @param string $sql SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @throws Exception */ public function executeQuery( string $sql, array $params = [], - $types = [], - ?QueryCacheProfile $qcp = null + array $types = [], + ?QueryCacheProfile $qcp = null, ): Result { if ($qcp !== null) { return $this->executeCacheQuery($sql, $params, $types, $qcp); } - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - if ($logger !== null) { - $logger->startQuery($sql, $params, $types); - } + $connection = $this->connect(); try { if (count($params) > 0) { - if ($this->needsArrayParameterConversion($params, $types)) { - [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); - } + [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); $stmt = $connection->prepare($sql); @@ -1104,33 +775,28 @@ public function executeQuery( return new Result($result, $this); } catch (Driver\Exception $e) { throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); - } finally { - if ($logger !== null) { - $logger->stopQuery(); - } } } /** * Executes a caching query. * - * @param string $sql SQL query - * @param list|array $params Query parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @throws CacheException * @throws Exception */ - public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp): Result + public function executeCacheQuery(string $sql, array $params, array $types, QueryCacheProfile $qcp): Result { $resultCache = $qcp->getResultCache() ?? $this->_config->getResultCache(); if ($resultCache === null) { - throw CacheException::noResultDriverConfigured(); + throw NoResultDriverConfigured::new(); } $connectionParams = $this->params; - unset($connectionParams['platform'], $connectionParams['password'], $connectionParams['url']); + unset($connectionParams['password']); [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); @@ -1177,28 +843,20 @@ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp) * * This method supports PDO binding types as well as DBAL mapping types. * - * @param string $sql SQL statement - * @param list|array $params Statement parameters - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * - * @return int|string The number of affected rows. + * @return int|numeric-string * * @throws Exception */ - public function executeStatement($sql, array $params = [], array $types = []) + public function executeStatement(string $sql, array $params = [], array $types = []): int|string { - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); - if ($logger !== null) { - $logger->startQuery($sql, $params, $types); - } + $connection = $this->connect(); try { if (count($params) > 0) { - if ($this->needsArrayParameterConversion($params, $types)) { - [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); - } + [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); $stmt = $connection->prepare($sql); @@ -1211,10 +869,6 @@ public function executeStatement($sql, array $params = [], array $types = []) return $connection->exec($sql); } catch (Driver\Exception $e) { throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); - } finally { - if ($logger !== null) { - $logger->stopQuery(); - } } } @@ -1223,37 +877,22 @@ public function executeStatement($sql, array $params = [], array $types = []) * * @return int The nesting level. A value of 0 means there's no active transaction. */ - public function getTransactionNestingLevel() + public function getTransactionNestingLevel(): int { return $this->transactionNestingLevel; } /** - * Returns the ID of the last inserted row, or the last value from a sequence object, - * depending on the underlying driver. - * - * Note: This method may not return a meaningful or consistent result across different drivers, - * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY - * columns or sequences. + * Returns the ID of the last inserted row. * - * @param string|null $name Name of the sequence object from which the ID should be returned. - * - * @return string|int|false A string representation of the last inserted ID. + * If the underlying driver does not support identity columns, an exception is thrown. * * @throws Exception */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - } - try { - return $this->getWrappedConnection()->lastInsertId($name); + return $this->connect()->lastInsertId(); } catch (Driver\Exception $e) { throw $this->convertException($e); } @@ -1275,7 +914,7 @@ public function lastInsertId($name = null) * * @template T */ - public function transactional(Closure $func) + public function transactional(Closure $func): mixed { $this->beginTransaction(); try { @@ -1293,186 +932,96 @@ public function transactional(Closure $func) /** * Sets if nested transactions should use savepoints. * - * @param bool $nestTransactionsWithSavepoints - * - * @return void + * @deprecated No replacement planned * * @throws Exception */ - public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) + public function setNestTransactionsWithSavepoints(bool $nestTransactionsWithSavepoints): void { if (! $nestTransactionsWithSavepoints) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5383', - <<<'DEPRECATION' - Nesting transactions without enabling savepoints is deprecated. - Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. - DEPRECATION, - self::class, - ); - } - - if ($this->transactionNestingLevel > 0) { - throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); - } - - if (! $this->getDatabasePlatform()->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); + throw new InvalidArgumentException(sprintf( + 'Calling %s with false to enable nesting transactions without savepoints is no longer supported.', + __METHOD__, + )); } - $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5383', + '%s is deprecated and will be removed in 5.0', + __METHOD__, + ); } /** * Gets if nested transactions should use savepoints. * - * @return bool + * @deprecated No replacement planned */ - public function getNestTransactionsWithSavepoints() + public function getNestTransactionsWithSavepoints(): bool { - return $this->nestTransactionsWithSavepoints; + Deprecation::trigger( + 'doctrine/dbal', + 'https://github.com/doctrine/dbal/pull/5383', + '%s is deprecated and will be removed in 5.0', + __METHOD__, + ); + + return true; } /** * Returns the savepoint name to use for nested transactions. - * - * @return string */ - protected function _getNestedTransactionSavePointName() + protected function _getNestedTransactionSavePointName(): string { return 'DOCTRINE_' . $this->transactionNestingLevel; } - /** - * @return bool - * - * @throws Exception - */ - public function beginTransaction() + /** @throws Exception */ + public function beginTransaction(): void { - $connection = $this->getWrappedConnection(); + $connection = $this->connect(); ++$this->transactionNestingLevel; - $logger = $this->_config->getSQLLogger(); - if ($this->transactionNestingLevel === 1) { - if ($logger !== null) { - $logger->startQuery('"START TRANSACTION"'); - } - $connection->beginTransaction(); - - if ($logger !== null) { - $logger->stopQuery(); - } - } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger !== null) { - $logger->startQuery('"SAVEPOINT"'); - } - - $this->createSavepoint($this->_getNestedTransactionSavePointName()); - if ($logger !== null) { - $logger->stopQuery(); - } } else { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5383', - <<<'DEPRECATION' - Nesting transactions without enabling savepoints is deprecated. - Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. - DEPRECATION, - self::class, - ); - } - - $eventManager = $this->getEventManager(); - - if ($eventManager->hasListeners(Events::onTransactionBegin)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onTransactionBegin, - ); - - $eventManager->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this)); + $this->createSavepoint($this->_getNestedTransactionSavePointName()); } - - return true; } - /** - * @return bool - * - * @throws Exception - */ - public function commit() + /** @throws Exception */ + public function commit(): void { if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); + throw NoActiveTransaction::new(); } if ($this->isRollbackOnly) { - throw ConnectionException::commitFailedRollbackOnly(); + throw CommitFailedRollbackOnly::new(); } - $result = true; - - $connection = $this->getWrappedConnection(); + $connection = $this->connect(); if ($this->transactionNestingLevel === 1) { - $result = $this->doCommit($connection); - } elseif ($this->nestTransactionsWithSavepoints) { + try { + $connection->commit(); + } catch (Driver\Exception $e) { + throw $this->convertException($e); + } + } else { $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); } --$this->transactionNestingLevel; - $eventManager = $this->getEventManager(); - - if ($eventManager->hasListeners(Events::onTransactionCommit)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onTransactionCommit, - ); - - $eventManager->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this)); - } - if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { - return $result; + return; } $this->beginTransaction(); - - return $result; - } - - /** - * @return bool - * - * @throws DriverException - */ - private function doCommit(DriverConnection $connection) - { - $logger = $this->_config->getSQLLogger(); - - if ($logger !== null) { - $logger->startQuery('"COMMIT"'); - } - - $result = $connection->commit(); - - if ($logger !== null) { - $logger->stopQuery(); - } - - return $result; } /** @@ -1495,67 +1044,33 @@ private function commitAll(): void } } - /** - * Cancels any database changes done during the current transaction. - * - * @return bool - * - * @throws Exception - */ - public function rollBack() + /** @throws Exception */ + public function rollBack(): void { if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); + throw NoActiveTransaction::new(); } - $connection = $this->getWrappedConnection(); - - $logger = $this->_config->getSQLLogger(); + $connection = $this->connect(); if ($this->transactionNestingLevel === 1) { - if ($logger !== null) { - $logger->startQuery('"ROLLBACK"'); - } - $this->transactionNestingLevel = 0; - $connection->rollBack(); - $this->isRollbackOnly = false; - if ($logger !== null) { - $logger->stopQuery(); - } - if ($this->autoCommit === false) { - $this->beginTransaction(); - } - } elseif ($this->nestTransactionsWithSavepoints) { - if ($logger !== null) { - $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); - } + try { + $connection->rollBack(); + } catch (Driver\Exception $e) { + throw $this->convertException($e); + } finally { + $this->isRollbackOnly = false; - $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); - --$this->transactionNestingLevel; - if ($logger !== null) { - $logger->stopQuery(); + if ($this->autoCommit === false) { + $this->beginTransaction(); + } } } else { - $this->isRollbackOnly = true; + $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); --$this->transactionNestingLevel; } - - $eventManager = $this->getEventManager(); - - if ($eventManager->hasListeners(Events::onTransactionRollBack)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onTransactionRollBack, - ); - - $eventManager->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this)); - } - - return true; } /** @@ -1563,16 +1078,14 @@ public function rollBack() * * @param string $savepoint The name of the savepoint to create. * - * @return void - * * @throws Exception */ - public function createSavepoint($savepoint) + public function createSavepoint(string $savepoint): void { $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); + throw SavepointsNotSupported::new(); } $this->executeStatement($platform->createSavePoint($savepoint)); @@ -1583,39 +1096,21 @@ public function createSavepoint($savepoint) * * @param string $savepoint The name of the savepoint to release. * - * @return void - * * @throws Exception */ - public function releaseSavepoint($savepoint) + public function releaseSavepoint(string $savepoint): void { - $logger = $this->_config->getSQLLogger(); - $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); + throw SavepointsNotSupported::new(); } if (! $platform->supportsReleaseSavepoints()) { - if ($logger !== null) { - $logger->stopQuery(); - } - return; } - if ($logger !== null) { - $logger->startQuery('"RELEASE SAVEPOINT"'); - } - $this->executeStatement($platform->releaseSavePoint($savepoint)); - - if ($logger === null) { - return; - } - - $logger->stopQuery(); } /** @@ -1623,57 +1118,29 @@ public function releaseSavepoint($savepoint) * * @param string $savepoint The name of the savepoint to rollback to. * - * @return void - * * @throws Exception */ - public function rollbackSavepoint($savepoint) + public function rollbackSavepoint(string $savepoint): void { $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { - throw ConnectionException::savepointsNotSupported(); + throw SavepointsNotSupported::new(); } $this->executeStatement($platform->rollbackSavePoint($savepoint)); } /** - * Gets the wrapped driver connection. - * - * @deprecated Use {@link getNativeConnection()} to access the native connection. + * Provides access to the native database connection. * - * @return DriverConnection + * @return resource|object * * @throws Exception */ - public function getWrappedConnection() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4966', - 'Connection::getWrappedConnection() is deprecated.' - . ' Use Connection::getNativeConnection() to access the native connection.', - ); - - $this->connect(); - - return $this->_conn; - } - - /** @return resource|object */ public function getNativeConnection() { - $this->connect(); - - if (! method_exists($this->_conn, 'getNativeConnection')) { - throw new LogicException(sprintf( - 'The driver connection %s does not support accessing the native connection.', - get_class($this->_conn), - )); - } - - return $this->_conn->getNativeConnection(); + return $this->connect()->getNativeConnection(); } /** @@ -1687,39 +1154,16 @@ public function createSchemaManager(): AbstractSchemaManager return $this->schemaManagerFactory->createSchemaManager($this); } - /** - * Gets the SchemaManager that can be used to inspect or change the - * database schema through the connection. - * - * @deprecated Use {@see createSchemaManager()} instead. - * - * @return AbstractSchemaManager - * - * @throws Exception - */ - public function getSchemaManager() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4515', - 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.', - ); - - return $this->_schemaManager ??= $this->createSchemaManager(); - } - /** * Marks the current transaction so that the only possible * outcome for the transaction to be rolled back. * - * @return void - * * @throws ConnectionException If no transaction is active. */ - public function setRollbackOnly() + public function setRollbackOnly(): void { if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); + throw NoActiveTransaction::new(); } $this->isRollbackOnly = true; @@ -1728,14 +1172,12 @@ public function setRollbackOnly() /** * Checks whether the current transaction is marked for rollback only. * - * @return bool - * * @throws ConnectionException If no transaction is active. */ - public function isRollbackOnly() + public function isRollbackOnly(): bool { if ($this->transactionNestingLevel === 0) { - throw ConnectionException::noActiveTransaction(); + throw NoActiveTransaction::new(); } return $this->isRollbackOnly; @@ -1752,7 +1194,7 @@ public function isRollbackOnly() * * @throws Exception */ - public function convertToDatabaseValue($value, $type) + public function convertToDatabaseValue(mixed $value, string $type): mixed { return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); } @@ -1768,7 +1210,7 @@ public function convertToDatabaseValue($value, $type) * * @throws Exception */ - public function convertToPHPValue($value, $type) + public function convertToPHPValue(mixed $value, string $type): mixed { return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); } @@ -1777,9 +1219,8 @@ public function convertToPHPValue($value, $type) * Binds a set of parameters, some or all of which are typed with a PDO binding type * or DBAL mapping type, to a given statement. * - * @param DriverStatement $stmt Prepared statement - * @param list|array $params Statement parameters - * @param array|array $types Parameter types + * @param list|array $params + * @param array|array $types * * @throws Exception */ @@ -1790,19 +1231,10 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ $bindIndex = 1; foreach ($params as $key => $value) { - if (isset($types[$key])) { + if (array_key_exists($key, $types)) { $type = $types[$key]; [$value, $bindingType] = $this->getBindingInfo($value, $type); } else { - if (array_key_exists($key, $types)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5550', - 'Using NULL as prepared statement parameter type is deprecated.' - . 'Omit or use ParameterType::STRING instead', - ); - } - $bindingType = ParameterType::STRING; } @@ -1813,19 +1245,10 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ } else { // Named parameters foreach ($params as $name => $value) { - if (isset($types[$name])) { + if (array_key_exists($name, $types)) { $type = $types[$name]; [$value, $bindingType] = $this->getBindingInfo($value, $type); } else { - if (array_key_exists($name, $types)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5550', - 'Using NULL as prepared statement parameter type is deprecated.' - . 'Omit or use ParameterType::STRING instead', - ); - } - $bindingType = ParameterType::STRING; } @@ -1837,14 +1260,14 @@ private function bindParameters(DriverStatement $stmt, array $params, array $typ /** * Gets the binding type of a given type. * - * @param mixed $value The value to bind. - * @param int|string|Type|null $type The type to bind (PDO or DBAL). + * @param mixed $value The value to bind. + * @param string|ParameterType|Type $type The type to bind. * - * @return array{mixed, int} [0] => the (escaped) value, [1] => the binding type. + * @return array{mixed, ParameterType} [0] => the (escaped) value, [1] => the binding type. * * @throws Exception */ - private function getBindingInfo($value, $type): array + private function getBindingInfo(mixed $value, string|ParameterType|Type $type): array { if (is_string($type)) { $type = Type::getType($type); @@ -1854,7 +1277,7 @@ private function getBindingInfo($value, $type): array $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); $bindingType = $type->getBindingType(); } else { - $bindingType = $type ?? ParameterType::STRING; + $bindingType = $type; } return [$value, $bindingType]; @@ -1862,10 +1285,8 @@ private function getBindingInfo($value, $type): array /** * Creates a new instance of a SQL query builder. - * - * @return QueryBuilder */ - public function createQueryBuilder() + public function createQueryBuilder(): QueryBuilder { return new Query\QueryBuilder($this); } @@ -1873,14 +1294,14 @@ public function createQueryBuilder() /** * @internal * - * @param list|array $params - * @param array|array $types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types */ final public function convertExceptionDuringQuery( Driver\Exception $e, string $sql, array $params = [], - array $types = [] + array $types = [], ): DriverException { return $this->handleDriverException($e, new Query($sql, $params, $types)); } @@ -1892,13 +1313,37 @@ final public function convertException(Driver\Exception $e): DriverException } /** - * @param array|array $params - * @param array|array $types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * - * @return array{string, list, array} + * @return array{ + * string, + * list|array, + * array, string|ParameterType|Type>|array + * } */ private function expandArrayParameters(string $sql, array $params, array $types): array { + $needsConversion = false; + $nonArrayTypes = []; + + if (is_string(key($params))) { + $needsConversion = true; + } else { + foreach ($types as $key => $type) { + if ($type instanceof ArrayParameterType) { + $needsConversion = true; + break; + } + + $nonArrayTypes[$key] = $type; + } + } + + if (! $needsConversion) { + return [$sql, $params, $nonArrayTypes]; + } + $this->parser ??= $this->getDatabasePlatform()->createSQLParser(); $visitor = new ExpandArrayParameters($params, $types); @@ -1911,35 +1356,11 @@ private function expandArrayParameters(string $sql, array $params, array $types) ]; } - /** - * @param array|array $params - * @param array|array $types - */ - private function needsArrayParameterConversion(array $params, array $types): bool - { - if (is_string(key($params))) { - return true; - } - - foreach ($types as $type) { - if ( - $type === ArrayParameterType::INTEGER - || $type === ArrayParameterType::STRING - || $type === ArrayParameterType::ASCII - || $type === ArrayParameterType::BINARY - ) { - return true; - } - } - - return false; - } - private function handleDriverException( Driver\Exception $driverException, - ?Query $query + ?Query $query, ): DriverException { - $this->exceptionConverter ??= $this->_driver->getExceptionConverter(); + $this->exceptionConverter ??= $this->driver->getExceptionConverter(); $exception = $this->exceptionConverter->convert($driverException, $query); if ($exception instanceof ConnectionLost) { @@ -1948,58 +1369,4 @@ private function handleDriverException( return $exception; } - - /** - * BC layer for a wide-spread use-case of old DBAL APIs - * - * @deprecated Use {@see executeStatement()} instead - * - * @param array $params The query parameters - * @param array $types The parameter types - */ - public function executeUpdate(string $sql, array $params = [], array $types = []): int - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - '%s is deprecated, please use executeStatement() instead.', - __METHOD__, - ); - - return $this->executeStatement($sql, $params, $types); - } - - /** - * BC layer for a wide-spread use-case of old DBAL APIs - * - * @deprecated Use {@see executeQuery()} instead - */ - public function query(string $sql): Result - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - '%s is deprecated, please use executeQuery() instead.', - __METHOD__, - ); - - return $this->executeQuery($sql); - } - - /** - * BC layer for a wide-spread use-case of old DBAL APIs - * - * @deprecated please use {@see executeStatement()} instead - */ - public function exec(string $sql): int - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4163', - '%s is deprecated, please use executeStatement() instead.', - __METHOD__, - ); - - return $this->executeStatement($sql); - } } diff --git a/doctrine/dbal/src/Connection/StaticServerVersionProvider.php b/doctrine/dbal/src/Connection/StaticServerVersionProvider.php new file mode 100644 index 000000000..aebe086a9 --- /dev/null +++ b/doctrine/dbal/src/Connection/StaticServerVersionProvider.php @@ -0,0 +1,19 @@ +version; + } +} diff --git a/doctrine/dbal/src/ConnectionException.php b/doctrine/dbal/src/ConnectionException.php index f1e18987b..de9624831 100644 --- a/doctrine/dbal/src/ConnectionException.php +++ b/doctrine/dbal/src/ConnectionException.php @@ -1,31 +1,10 @@ */ - protected $connections = ['primary' => null, 'replica' => null]; + protected array $connections = ['primary' => null, 'replica' => null]; /** * You can keep the replica connection and then switch back to it * during the request if you know what you are doing. - * - * @var bool */ - protected $keepReplica = false; + protected bool $keepReplica = false; /** * Creates Primary Replica Connection. * * @internal The connection can be only instantiated by the driver manager. * - * @param array $params + * @param array $params * @psalm-param Params $params - * - * @throws Exception - * @throws InvalidArgumentException */ - public function __construct( - array $params, - Driver $driver, - ?Configuration $config = null, - ?EventManager $eventManager = null - ) { + public function __construct(array $params, Driver $driver, ?Configuration $config = null) + { if (! isset($params['replica'], $params['primary'])) { throw new InvalidArgumentException('primary or replica configuration missing'); } @@ -125,9 +116,9 @@ public function __construct( } } - $this->keepReplica = (bool) ($params['keepReplica'] ?? false); + $this->keepReplica = ! empty($params['keepReplica']); - parent::__construct($params, $driver, $config, $eventManager); + parent::__construct($params, $driver, $config); } /** @@ -138,12 +129,7 @@ public function isConnectedToPrimary(): bool return $this->_conn !== null && $this->_conn === $this->connections['primary']; } - /** - * @param string|null $connectionName - * - * @return bool - */ - public function connect($connectionName = null) + public function connect(?string $connectionName = null): DriverConnection { if ($connectionName !== null) { throw new InvalidArgumentException( @@ -155,10 +141,10 @@ public function connect($connectionName = null) return $this->performConnect(); } - protected function performConnect(?string $connectionName = null): bool + protected function performConnect(?string $connectionName = null): DriverConnection { $requestedConnectionChange = ($connectionName !== null); - $connectionName = $connectionName ?? 'replica'; + $connectionName ??= 'replica'; if ($connectionName !== 'replica' && $connectionName !== 'primary') { throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.'); @@ -168,7 +154,7 @@ protected function performConnect(?string $connectionName = null): bool // change request, then abort right here, because we are already done. // This prevents writes to the replica in case of "keepReplica" option enabled. if ($this->_conn !== null && ! $requestedConnectionChange) { - return false; + return $this->_conn; } $forcePrimaryAsReplica = false; @@ -185,7 +171,7 @@ protected function performConnect(?string $connectionName = null): bool $this->connections['replica'] = $this->_conn; } - return false; + return $this->_conn; } if ($connectionName === 'primary') { @@ -199,19 +185,7 @@ protected function performConnect(?string $connectionName = null): bool $this->connections['replica'] = $this->_conn = $this->connectTo($connectionName); } - if ($this->_eventManager->hasListeners(Events::postConnect)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated. Implement a middleware instead.', - Events::postConnect, - ); - - $eventArgs = new ConnectionEventArgs($this); - $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); - } - - return true; + return $this->_conn; } /** @@ -219,9 +193,9 @@ protected function performConnect(?string $connectionName = null): bool * * All following statements after this will be executed against the primary node. */ - public function ensureConnectedToPrimary(): bool + public function ensureConnectedToPrimary(): void { - return $this->performConnect('primary'); + $this->performConnect('primary'); } /** @@ -231,101 +205,89 @@ public function ensureConnectedToPrimary(): bool * unless the keepReplica option is set to false and a primary connection * was already opened. */ - public function ensureConnectedToReplica(): bool + public function ensureConnectedToReplica(): void { - return $this->performConnect('replica'); + $this->performConnect('replica'); } /** * Connects to a specific connection. * - * @param string $connectionName - * - * @return DriverConnection - * * @throws Exception */ - protected function connectTo($connectionName) + protected function connectTo(string $connectionName): DriverConnection { $params = $this->getParams(); + assert(isset($params['primary'])); - $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); + if ($connectionName === 'primary') { + $connectionParams = $params['primary']; + } else { + assert(isset($params['replica'])); + $connectionParams = $this->chooseReplicaConnectionParameters($params['primary'], $params['replica']); + } try { - return $this->_driver->connect($connectionParams); + return $this->driver->connect($connectionParams); } catch (DriverException $e) { throw $this->convertException($e); } } /** - * @param string $connectionName - * @param mixed[] $params + * @param OverrideParams $primary + * @param array $replicas * - * @return mixed + * @return array + * @psalm-return OverrideParams */ - protected function chooseConnectionConfiguration( - $connectionName, + protected function chooseReplicaConnectionParameters( #[SensitiveParameter] - $params - ) { - if ($connectionName === 'primary') { - return $params['primary']; - } - - $config = $params['replica'][array_rand($params['replica'])]; + array $primary, + #[SensitiveParameter] + array $replicas, + ): array { + $params = $replicas[array_rand($replicas)]; - if (! isset($config['charset']) && isset($params['primary']['charset'])) { - $config['charset'] = $params['primary']['charset']; + if (! isset($params['charset']) && isset($primary['charset'])) { + $params['charset'] = $primary['charset']; } - return $config; + return $params; } /** * {@inheritDoc} */ - public function executeStatement($sql, array $params = [], array $types = []) + public function executeStatement(string $sql, array $params = [], array $types = []): int|string { $this->ensureConnectedToPrimary(); return parent::executeStatement($sql, $params, $types); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): void { $this->ensureConnectedToPrimary(); - return parent::beginTransaction(); + parent::beginTransaction(); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { $this->ensureConnectedToPrimary(); - return parent::commit(); + parent::commit(); } - /** - * {@inheritDoc} - */ - public function rollBack() + public function rollBack(): void { $this->ensureConnectedToPrimary(); - return parent::rollBack(); + parent::rollBack(); } - /** - * {@inheritDoc} - */ - public function close() + public function close(): void { unset($this->connections['primary'], $this->connections['replica']); @@ -335,30 +297,21 @@ public function close() $this->connections = ['primary' => null, 'replica' => null]; } - /** - * {@inheritDoc} - */ - public function createSavepoint($savepoint) + public function createSavepoint(string $savepoint): void { $this->ensureConnectedToPrimary(); parent::createSavepoint($savepoint); } - /** - * {@inheritDoc} - */ - public function releaseSavepoint($savepoint) + public function releaseSavepoint(string $savepoint): void { $this->ensureConnectedToPrimary(); parent::releaseSavepoint($savepoint); } - /** - * {@inheritDoc} - */ - public function rollbackSavepoint($savepoint) + public function rollbackSavepoint(string $savepoint): void { $this->ensureConnectedToPrimary(); diff --git a/doctrine/dbal/src/Driver.php b/doctrine/dbal/src/Driver.php index 46e422ba9..ffdc83cf0 100644 --- a/doctrine/dbal/src/Driver.php +++ b/doctrine/dbal/src/Driver.php @@ -1,12 +1,13 @@ getCode()) { - case -104: - return new SyntaxErrorException($exception, $query); - - case -203: - return new NonUniqueFieldNameException($exception, $query); - - case -204: - return new TableNotFoundException($exception, $query); - - case -206: - return new InvalidFieldNameException($exception, $query); - - case -407: - return new NotNullConstraintViolationException($exception, $query); - - case -530: - case -531: - case -532: - case -20356: - return new ForeignKeyConstraintViolationException($exception, $query); - - case -601: - return new TableExistsException($exception, $query); - - case -803: - return new UniqueConstraintViolationException($exception, $query); - - case -1336: - case -30082: - return new ConnectionException($exception, $query); - } - - return new DriverException($exception, $query); + return match ($exception->getCode()) { + -104 => new SyntaxErrorException($exception, $query), + -203 => new NonUniqueFieldNameException($exception, $query), + -204 => new TableNotFoundException($exception, $query), + -206 => new InvalidFieldNameException($exception, $query), + -407 => new NotNullConstraintViolationException($exception, $query), + -530, + -531, + -532, + -20356 => new ForeignKeyConstraintViolationException($exception, $query), + -601 => new TableExistsException($exception, $query), + -803 => new UniqueConstraintViolationException($exception, $query), + -1336, + -30082 => new ConnectionException($exception, $query), + default => new DriverException($exception, $query), + }; } } diff --git a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php index 87d50aff9..254489834 100644 --- a/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/MySQL/ExceptionConverter.php @@ -31,89 +31,63 @@ final class ExceptionConverter implements ExceptionConverterInterface */ public function convert(Exception $exception, ?Query $query): DriverException { - switch ($exception->getCode()) { - case 1008: - return new DatabaseDoesNotExist($exception, $query); - - case 1213: - return new DeadlockException($exception, $query); - - case 1205: - return new LockWaitTimeoutException($exception, $query); - - case 1050: - return new TableExistsException($exception, $query); - - case 1051: - case 1146: - return new TableNotFoundException($exception, $query); - - case 1216: - case 1217: - case 1451: - case 1452: - case 1701: - return new ForeignKeyConstraintViolationException($exception, $query); - - case 1062: - case 1557: - case 1569: - case 1586: - return new UniqueConstraintViolationException($exception, $query); - - case 1054: - case 1166: - case 1611: - return new InvalidFieldNameException($exception, $query); - - case 1052: - case 1060: - case 1110: - return new NonUniqueFieldNameException($exception, $query); - - case 1064: - case 1149: - case 1287: - case 1341: - case 1342: - case 1343: - case 1344: - case 1382: - case 1479: - case 1541: - case 1554: - case 1626: - return new SyntaxErrorException($exception, $query); - - case 1044: - case 1045: - case 1046: - case 1049: - case 1095: - case 1142: - case 1143: - case 1227: - case 1370: - case 1429: - case 2002: - case 2005: - case 2054: - return new ConnectionException($exception, $query); - - case 2006: - return new ConnectionLost($exception, $query); - - case 1048: - case 1121: - case 1138: - case 1171: - case 1252: - case 1263: - case 1364: - case 1566: - return new NotNullConstraintViolationException($exception, $query); - } - - return new DriverException($exception, $query); + return match ($exception->getCode()) { + 1008 => new DatabaseDoesNotExist($exception, $query), + 1213 => new DeadlockException($exception, $query), + 1205 => new LockWaitTimeoutException($exception, $query), + 1050 => new TableExistsException($exception, $query), + 1051, + 1146 => new TableNotFoundException($exception, $query), + 1216, + 1217, + 1451, + 1452, + 1701 => new ForeignKeyConstraintViolationException($exception, $query), + 1062, + 1557, + 1569, + 1586 => new UniqueConstraintViolationException($exception, $query), + 1054, + 1166, + 1611 => new InvalidFieldNameException($exception, $query), + 1052, + 1060, + 1110 => new NonUniqueFieldNameException($exception, $query), + 1064, + 1149, + 1287, + 1341, + 1342, + 1343, + 1344, + 1382, + 1479, + 1541, + 1554, + 1626 => new SyntaxErrorException($exception, $query), + 1044, + 1045, + 1046, + 1049, + 1095, + 1142, + 1143, + 1227, + 1370, + 1429, + 2002, + 2005, + 2054 => new ConnectionException($exception, $query), + 2006 => new ConnectionLost($exception, $query), + 1048, + 1121, + 1138, + 1171, + 1252, + 1263, + 1364, + 1566 => new NotNullConstraintViolationException($exception, $query), + default => new DriverException($exception, $query), + }; } } diff --git a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php index 4703a57d5..1c0dc793f 100644 --- a/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/OCI/ExceptionConverter.php @@ -26,49 +26,27 @@ final class ExceptionConverter implements ExceptionConverterInterface /** @link http://www.dba-oracle.com/t_error_code_list.htm */ public function convert(Exception $exception, ?Query $query): DriverException { - switch ($exception->getCode()) { - case 1: - case 2299: - case 38911: - return new UniqueConstraintViolationException($exception, $query); - - case 904: - return new InvalidFieldNameException($exception, $query); - - case 918: - case 960: - return new NonUniqueFieldNameException($exception, $query); - - case 923: - return new SyntaxErrorException($exception, $query); - - case 942: - return new TableNotFoundException($exception, $query); - - case 955: - return new TableExistsException($exception, $query); - - case 1017: - case 12545: - return new ConnectionException($exception, $query); - - case 1400: - return new NotNullConstraintViolationException($exception, $query); - - case 1918: - return new DatabaseDoesNotExist($exception, $query); - - case 2289: - case 2443: - case 4080: - return new DatabaseObjectNotFoundException($exception, $query); - - case 2266: - case 2291: - case 2292: - return new ForeignKeyConstraintViolationException($exception, $query); - } - - return new DriverException($exception, $query); + return match ($exception->getCode()) { + 1, + 2299, + 38911 => new UniqueConstraintViolationException($exception, $query), + 904 => new InvalidFieldNameException($exception, $query), + 918, + 960 => new NonUniqueFieldNameException($exception, $query), + 923 => new SyntaxErrorException($exception, $query), + 942 => new TableNotFoundException($exception, $query), + 955 => new TableExistsException($exception, $query), + 1017, + 12545 => new ConnectionException($exception, $query), + 1400 => new NotNullConstraintViolationException($exception, $query), + 1918 => new DatabaseDoesNotExist($exception, $query), + 2289, + 2443, + 4080 => new DatabaseObjectNotFoundException($exception, $query), + 2266, + 2291, + 2292 => new ForeignKeyConstraintViolationException($exception, $query), + default => new DriverException($exception, $query), + }; } } diff --git a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php index 2baca1ee2..54e496638 100644 --- a/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php @@ -21,7 +21,7 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; -use function strpos; +use function str_contains; /** @internal */ final class ExceptionConverter implements ExceptionConverterInterface @@ -37,7 +37,7 @@ public function convert(Exception $exception, ?Query $query): DriverException case '0A000': // Foreign key constraint violations during a TRUNCATE operation // are considered "feature not supported" in PostgreSQL. - if (strpos($exception->getMessage(), 'truncate') !== false) { + if (str_contains($exception->getMessage(), 'truncate')) { return new ForeignKeyConstraintViolationException($exception, $query); } @@ -77,13 +77,6 @@ public function convert(Exception $exception, ?Query $query): DriverException return new ConnectionException($exception, $query); } - // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.4.10), - // in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code. - // We have to match against the SQLSTATE in the error message in these cases. - if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { - return new ConnectionException($exception, $query); - } - return new DriverException($exception, $query); } } diff --git a/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php index d0e8e9f46..561e58b96 100644 --- a/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/SQLSrv/ExceptionConverter.php @@ -28,42 +28,22 @@ final class ExceptionConverter implements ExceptionConverterInterface { public function convert(Exception $exception, ?Query $query): DriverException { - switch ($exception->getCode()) { - case 102: - return new SyntaxErrorException($exception, $query); - - case 207: - return new InvalidFieldNameException($exception, $query); - - case 208: - return new TableNotFoundException($exception, $query); - - case 209: - return new NonUniqueFieldNameException($exception, $query); - - case 515: - return new NotNullConstraintViolationException($exception, $query); - - case 547: - case 4712: - return new ForeignKeyConstraintViolationException($exception, $query); - - case 2601: - case 2627: - return new UniqueConstraintViolationException($exception, $query); - - case 2714: - return new TableExistsException($exception, $query); - - case 3701: - case 15151: - return new DatabaseObjectNotFoundException($exception, $query); - - case 11001: - case 18456: - return new ConnectionException($exception, $query); - } - - return new DriverException($exception, $query); + return match ($exception->getCode()) { + 102 => new SyntaxErrorException($exception, $query), + 207 => new InvalidFieldNameException($exception, $query), + 208 => new TableNotFoundException($exception, $query), + 209 => new NonUniqueFieldNameException($exception, $query), + 515 => new NotNullConstraintViolationException($exception, $query), + 547, + 4712 => new ForeignKeyConstraintViolationException($exception, $query), + 2601, + 2627 => new UniqueConstraintViolationException($exception, $query), + 2714 => new TableExistsException($exception, $query), + 3701, + 15151 => new DatabaseObjectNotFoundException($exception, $query), + 11001, + 18456 => new ConnectionException($exception, $query), + default => new DriverException($exception, $query), + }; } } diff --git a/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php b/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php index 9e67155ad..58851955f 100644 --- a/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php +++ b/doctrine/dbal/src/Driver/API/SQLite/ExceptionConverter.php @@ -20,7 +20,7 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use Doctrine\DBAL\Query; -use function strpos; +use function str_contains; /** @internal */ final class ExceptionConverter implements ExceptionConverterInterface @@ -28,55 +28,55 @@ final class ExceptionConverter implements ExceptionConverterInterface /** @link http://www.sqlite.org/c3ref/c_abort.html */ public function convert(Exception $exception, ?Query $query): DriverException { - if (strpos($exception->getMessage(), 'database is locked') !== false) { + if (str_contains($exception->getMessage(), 'database is locked')) { return new LockWaitTimeoutException($exception, $query); } if ( - strpos($exception->getMessage(), 'must be unique') !== false || - strpos($exception->getMessage(), 'is not unique') !== false || - strpos($exception->getMessage(), 'are not unique') !== false || - strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false + str_contains($exception->getMessage(), 'must be unique') || + str_contains($exception->getMessage(), 'is not unique') || + str_contains($exception->getMessage(), 'are not unique') || + str_contains($exception->getMessage(), 'UNIQUE constraint failed') ) { return new UniqueConstraintViolationException($exception, $query); } if ( - strpos($exception->getMessage(), 'may not be NULL') !== false || - strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false + str_contains($exception->getMessage(), 'may not be NULL') || + str_contains($exception->getMessage(), 'NOT NULL constraint failed') ) { return new NotNullConstraintViolationException($exception, $query); } - if (strpos($exception->getMessage(), 'no such table:') !== false) { + if (str_contains($exception->getMessage(), 'no such table:')) { return new TableNotFoundException($exception, $query); } - if (strpos($exception->getMessage(), 'already exists') !== false) { + if (str_contains($exception->getMessage(), 'already exists')) { return new TableExistsException($exception, $query); } - if (strpos($exception->getMessage(), 'has no column named') !== false) { + if (str_contains($exception->getMessage(), 'has no column named')) { return new InvalidFieldNameException($exception, $query); } - if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { + if (str_contains($exception->getMessage(), 'ambiguous column name')) { return new NonUniqueFieldNameException($exception, $query); } - if (strpos($exception->getMessage(), 'syntax error') !== false) { + if (str_contains($exception->getMessage(), 'syntax error')) { return new SyntaxErrorException($exception, $query); } - if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { + if (str_contains($exception->getMessage(), 'attempt to write a readonly database')) { return new ReadOnlyException($exception, $query); } - if (strpos($exception->getMessage(), 'unable to open database file') !== false) { + if (str_contains($exception->getMessage(), 'unable to open database file')) { return new ConnectionException($exception, $query); } - if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) { + if (str_contains($exception->getMessage(), 'FOREIGN KEY constraint failed')) { return new ForeignKeyConstraintViolationException($exception, $query); } diff --git a/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php b/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php deleted file mode 100644 index 3779c8bab..000000000 --- a/doctrine/dbal/src/Driver/API/SQLite/UserDefinedFunctions.php +++ /dev/null @@ -1,80 +0,0 @@ - ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], - 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], - 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], - ]; - - /** - * @param callable(string, callable, int): bool $callback - * @param array $additionalFunctions - */ - public static function register(callable $callback, array $additionalFunctions = []): void - { - $userDefinedFunctions = array_merge(self::DEFAULT_FUNCTIONS, $additionalFunctions); - - foreach ($userDefinedFunctions as $function => $data) { - $callback($function, $data['callback'], $data['numArgs']); - } - } - - /** - * User-defined function that implements MOD(). - * - * @param int $a - * @param int $b - */ - public static function mod($a, $b): int - { - return $a % $b; - } - - /** - * User-defined function that implements LOCATE(). - * - * @param string $str - * @param string $substr - * @param int $offset - */ - public static function locate($str, $substr, $offset = 0): int - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5749', - 'Relying on DBAL\'s emulated LOCATE() function is deprecated. ' - . 'Use INSTR() or %s::getLocateExpression() instead.', - AbstractPlatform::class, - ); - - // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. - // So we have to make them compatible if an offset is given. - if ($offset > 0) { - $offset -= 1; - } - - $pos = strpos($str, $substr, $offset); - - if ($pos !== false) { - return $pos + 1; - } - - return 0; - } -} diff --git a/doctrine/dbal/src/Driver/AbstractDB2Driver.php b/doctrine/dbal/src/Driver/AbstractDB2Driver.php index 79efb8650..9955a3836 100644 --- a/doctrine/dbal/src/Driver/AbstractDB2Driver.php +++ b/doctrine/dbal/src/Driver/AbstractDB2Driver.php @@ -1,100 +1,27 @@ getVersionNumber($version), '11.1', '>=')) { - return new DB2111Platform(); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5156', - 'IBM DB2 < 11.1 support is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to IBM DB2 11.1 or later.', - ); - - return $this->getDatabasePlatform(); - } - - /** - * Detects IBM DB2 server version - * - * @param string $versionString Version string as returned by IBM DB2 server, i.e. 'DB2/LINUXX8664 11.5.8.0' - * - * @throws DBALException - */ - private function getVersionNumber(string $versionString): string - { - if ( - preg_match( - '/^(?:[^\s]+\s)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', - $versionString, - $versionParts, - ) === 0 - ) { - throw DBALException::invalidPlatformVersionSpecified( - $versionString, - '^(?:[^\s]+\s)?..', - ); - } - - return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; - } } diff --git a/doctrine/dbal/src/Driver/AbstractException.php b/doctrine/dbal/src/Driver/AbstractException.php index 389f82e70..367fc497a 100644 --- a/doctrine/dbal/src/Driver/AbstractException.php +++ b/doctrine/dbal/src/Driver/AbstractException.php @@ -8,36 +8,28 @@ use Throwable; /** - * Base implementation of the {@see Exception} interface. - * - * @internal + * Abstract base implementation of the {@see DriverException} interface. * * @psalm-immutable */ abstract class AbstractException extends BaseException implements Exception { - /** - * The SQLSTATE of the driver. - */ - private ?string $sqlState = null; - /** * @param string $message The driver error message. * @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any. * @param int $code The driver specific error code if any. * @param Throwable|null $previous The previous throwable used for the exception chaining. */ - public function __construct($message, $sqlState = null, $code = 0, ?Throwable $previous = null) - { + public function __construct( + string $message, + private readonly ?string $sqlState = null, + int $code = 0, + ?Throwable $previous = null, + ) { parent::__construct($message, $code, $previous); - - $this->sqlState = $sqlState; } - /** - * {@inheritDoc} - */ - public function getSQLState() + public function getSQLState(): ?string { return $this->sqlState; } diff --git a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php index 83159a540..40d650763 100644 --- a/doctrine/dbal/src/Driver/AbstractMySQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractMySQLDriver.php @@ -1,25 +1,21 @@ getServerVersion(); + if (stripos($version, 'mariadb') !== false) { $mariaDbVersion = $this->getMariaDbMysqlVersionNumber($version); if (version_compare($mariaDbVersion, '10.6.0', '>=')) { - return new MariaDb1060Platform(); + return new MariaDB1060Platform(); } if (version_compare($mariaDbVersion, '10.5.2', '>=')) { - return new MariaDb1052Platform(); - } - - if (version_compare($mariaDbVersion, '10.4.3', '>=')) { - return new MariaDb1043Platform(); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6110', - 'Support for MariaDB < 10.4 is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to a more recent version of MariaDB.', - ); - - if (version_compare($mariaDbVersion, '10.2.7', '>=')) { - return new MariaDb1027Platform(); - } - } else { - $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version); - if (version_compare($oracleMysqlVersion, '8', '>=')) { - if (! version_compare($version, '8.0.0', '>=')) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/dbal/pull/5779', - 'Version detection logic for MySQL will change in DBAL 4. ' - . 'Please specify the version as the server reports it, e.g. "8.0.31" instead of "8".', - ); - } - - return new MySQL80Platform(); + return new MariaDB1052Platform(); } - if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) { - if (! version_compare($version, '5.7.9', '>=')) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/dbal/pull/5779', - 'Version detection logic for MySQL will change in DBAL 4. ' - . 'Please specify the version as the server reports it, e.g. "5.7.40" instead of "5.7".', - ); - } - - return new MySQL57Platform(); - } + return new MariaDBPlatform(); + } - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5072', - 'MySQL 5.6 support is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to MySQL 5.7 or later.', - ); + if (version_compare($version, '8.0.0', '>=')) { + return new MySQL80Platform(); } - return $this->getDatabasePlatform(); + return new MySQLPlatform(); } - /** - * Get a normalized 'version number' from the server string - * returned by Oracle MySQL servers. - * - * @param string $versionString Version string returned by the driver, i.e. '5.7.10' - * - * @throws Exception - */ - private function getOracleMysqlVersionNumber(string $versionString): string + public function getExceptionConverter(): ExceptionConverterInterface { - if ( - preg_match( - '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', - $versionString, - $versionParts, - ) === 0 - ) { - throw Exception::invalidPlatformVersionSpecified( - $versionString, - '..', - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? null; - - if ($majorVersion === '5' && $minorVersion === '7') { - $patchVersion ??= '9'; - } - - return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; + return new ExceptionConverter(); } /** @@ -141,20 +64,10 @@ private function getOracleMysqlVersionNumber(string $versionString): string * * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' * - * @throws Exception + * @throws InvalidPlatformVersion */ private function getMariaDbMysqlVersionNumber(string $versionString): string { - if (stripos($versionString, 'MariaDB') === 0) { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/dbal/pull/5779', - 'Version detection logic for MySQL will change in DBAL 4. ' - . 'Please specify the version as the server reports it, ' - . 'e.g. "10.9.3-MariaDB" instead of "mariadb-10.9".', - ); - } - if ( preg_match( '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', @@ -162,7 +75,7 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string $versionParts, ) === 0 ) { - throw Exception::invalidPlatformVersionSpecified( + throw InvalidPlatformVersion::new( $versionString, '^(?:5\.5\.5-)?(mariadb-)?..', ); @@ -170,40 +83,4 @@ private function getMariaDbMysqlVersionNumber(string $versionString): string return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; } - - /** - * {@inheritDoc} - * - * @return AbstractMySQLPlatform - */ - public function getDatabasePlatform() - { - return new MySQLPlatform(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link AbstractMySQLPlatform::createSchemaManager()} instead. - * - * @return MySQLSchemaManager - */ - public function getSchemaManager(Connection $conn, AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5458', - 'AbstractMySQLDriver::getSchemaManager() is deprecated.' - . ' Use MySQLPlatform::createSchemaManager() instead.', - ); - - assert($platform instanceof AbstractMySQLPlatform); - - return new MySQLSchemaManager($conn, $platform); - } - - public function getExceptionConverter(): ExceptionConverter - { - return new MySQL\ExceptionConverter(); - } } diff --git a/doctrine/dbal/src/Driver/AbstractOracleDriver.php b/doctrine/dbal/src/Driver/AbstractOracleDriver.php index b0f92453a..cf56cfad6 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver.php @@ -1,64 +1,37 @@ $params The connection parameters to return the Easy Connect String for. - * - * @return string */ - protected function getEasyConnectString(array $params) + protected function getEasyConnectString(array $params): string { return (string) EasyConnectString::fromConnectionParameters($params); } diff --git a/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php b/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php index 91bc6a7e1..a7778176c 100644 --- a/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php +++ b/doctrine/dbal/src/Driver/AbstractOracleDriver/EasyConnectString.php @@ -15,11 +15,8 @@ */ final class EasyConnectString { - private string $string; - - private function __construct(string $string) + private function __construct(private readonly string $string) { - $this->string = $string; } public function __toString(): string @@ -104,8 +101,7 @@ private static function renderParams(array $params): string return implode('', $chunks); } - /** @param mixed $value */ - private static function renderValue($value): string + private static function renderValue(mixed $value): string { if (is_array($value)) { return self::renderParams($value); diff --git a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php index 099630d33..2efcddca7 100644 --- a/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php +++ b/doctrine/dbal/src/Driver/AbstractPostgreSQLDriver.php @@ -1,88 +1,27 @@ \d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts) === 0) { - throw Exception::invalidPlatformVersionSpecified( - $version, - '..', - ); - } - - $majorVersion = $versionParts['major']; - $minorVersion = $versionParts['minor'] ?? 0; - $patchVersion = $versionParts['patch'] ?? 0; - $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; - - if (version_compare($version, '10.0', '>=')) { - return new PostgreSQL100Platform(); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5060', - 'PostgreSQL 9 support is deprecated and will be removed in DBAL 4.' - . ' Consider upgrading to Postgres 10 or later.', - ); - - return new PostgreSQL94Platform(); - } - - /** - * {@inheritDoc} - */ - public function getDatabasePlatform() + public function getDatabasePlatform(ServerVersionProvider $versionProvider): PostgreSQLPlatform { - return new PostgreSQL94Platform(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link PostgreSQLPlatform::createSchemaManager()} instead. - */ - public function getSchemaManager(Connection $conn, AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5458', - 'AbstractPostgreSQLDriver::getSchemaManager() is deprecated.' - . ' Use PostgreSQLPlatform::createSchemaManager() instead.', - ); - - assert($platform instanceof PostgreSQLPlatform); - - return new PostgreSQLSchemaManager($conn, $platform); + return new PostgreSQLPlatform(); } - public function getExceptionConverter(): ExceptionConverter + public function getExceptionConverter(): ExceptionConverterInterface { - return new PostgreSQL\ExceptionConverter(); + return new ExceptionConverter(); } } diff --git a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php index b9a99552e..8c2d012fe 100644 --- a/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php +++ b/doctrine/dbal/src/Driver/AbstractSQLServerDriver.php @@ -1,49 +1,23 @@ fetchNumeric(); diff --git a/doctrine/dbal/src/Driver/IBMDB2/Connection.php b/doctrine/dbal/src/Driver/IBMDB2/Connection.php index dfb11c236..2c8783b28 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Connection.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Connection.php @@ -1,15 +1,14 @@ connection = $connection; } - /** - * {@inheritDoc} - */ - public function getServerVersion() + public function getServerVersion(): string { $serverInfo = db2_server_info($this->connection); assert($serverInfo instanceof stdClass); @@ -53,7 +45,7 @@ public function getServerVersion() return $serverInfo->DBMS_VER; } - public function prepare(string $sql): DriverStatement + public function prepare(string $sql): Statement { $stmt = @db2_prepare($this->connection, $sql); @@ -64,26 +56,17 @@ public function prepare(string $sql): DriverStatement return new Statement($stmt); } - public function query(string $sql): ResultInterface + public function query(string $sql): Result { return $this->prepare($sql)->execute(); } - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - $value = db2_escape_string($value); - - if ($type === ParameterType::INTEGER) { - return $value; - } - - return "'" . $value . "'"; + return "'" . db2_escape_string($value) . "'"; } - public function exec(string $sql): int + public function exec(string $sql): int|string { $stmt = @db2_exec($this->connection, $sql); @@ -91,46 +74,53 @@ public function exec(string $sql): int throw StatementError::new(); } - return db2_num_rows($stmt); + $numRows = db2_num_rows($stmt); + + if ($numRows === false) { + throw StatementError::new(); + } + + return $numRows; } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) + public function lastInsertId(): string { - if ($name !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); + $lastInsertId = db2_last_insert_id($this->connection); + + if ($lastInsertId === null) { + throw NoIdentityValue::new(); } - return db2_last_insert_id($this->connection) ?? false; + return $lastInsertId; } - public function beginTransaction(): bool + public function beginTransaction(): void { - return db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF); + if (db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF) !== true) { + throw ConnectionError::new($this->connection); + } } - public function commit(): bool + public function commit(): void { if (! db2_commit($this->connection)) { throw ConnectionError::new($this->connection); } - return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); + if (db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON) !== true) { + throw ConnectionError::new($this->connection); + } } - public function rollBack(): bool + public function rollBack(): void { if (! db2_rollback($this->connection)) { throw ConnectionError::new($this->connection); } - return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); + if (db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON) !== true) { + throw ConnectionError::new($this->connection); + } } /** @return resource */ diff --git a/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php b/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php index 124a6f6db..a1e594821 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php +++ b/doctrine/dbal/src/Driver/IBMDB2/DataSourceName.php @@ -8,20 +8,17 @@ use function implode; use function sprintf; -use function strpos; +use function str_contains; /** * IBM DB2 DSN */ final class DataSourceName { - private string $string; - private function __construct( #[SensitiveParameter] - string $string + private readonly string $string, ) { - $this->string = $string; } public function toString(): string @@ -36,7 +33,7 @@ public function toString(): string */ public static function fromArray( #[SensitiveParameter] - array $params + array $params, ): self { $chunks = []; @@ -52,11 +49,10 @@ public static function fromArray( * * @param array $params */ - public static function fromConnectionParameters( - #[SensitiveParameter] - array $params - ): self { - if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) { + public static function fromConnectionParameters(#[SensitiveParameter] + array $params,): self + { + if (isset($params['dbname']) && str_contains($params['dbname'], '=')) { return new self($params['dbname']); } diff --git a/doctrine/dbal/src/Driver/IBMDB2/Driver.php b/doctrine/dbal/src/Driver/IBMDB2/Driver.php index 7650db5f4..f2f4ed770 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Driver.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Driver.php @@ -1,5 +1,7 @@ toString(); $username = $params['user'] ?? ''; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php index 231c9d473..c584fb82c 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.php @@ -13,7 +13,7 @@ */ final class CannotCopyStreamToStream extends AbstractException { - /** @psalm-param array{message: string}|null $error */ + /** @psalm-param array{message: string, ...}|null $error */ public static function new(?array $error): self { $message = 'Could not copy source stream to temporary file'; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php index 63f7ca1e2..d7646a0ce 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.php @@ -13,7 +13,7 @@ */ final class CannotCreateTemporaryFile extends AbstractException { - /** @psalm-param array{message: string}|null $error */ + /** @psalm-param array{message: string, ...}|null $error */ public static function new(?array $error): self { $message = 'Could not create temporary file'; diff --git a/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php b/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php index 42df5e15c..5344b6529 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Exception/PrepareFailed.php @@ -13,7 +13,7 @@ */ final class PrepareFailed extends AbstractException { - /** @psalm-param array{message: string}|null $error */ + /** @psalm-param array{message: string, ...}|null $error */ public static function new(?array $error): self { if ($error === null) { diff --git a/doctrine/dbal/src/Driver/IBMDB2/Result.php b/doctrine/dbal/src/Driver/IBMDB2/Result.php index d8e9fc599..461f44aca 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Result.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Result.php @@ -17,23 +17,16 @@ final class Result implements ResultInterface { - /** @var resource */ - private $statement; - /** * @internal The result can be only instantiated by its driver connection or statement. * * @param resource $statement */ - public function __construct($statement) + public function __construct(private readonly mixed $statement) { - $this->statement = $statement; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { $row = @db2_fetch_array($this->statement); @@ -44,10 +37,7 @@ public function fetchNumeric() return $row; } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { $row = @db2_fetch_assoc($this->statement); @@ -58,10 +48,7 @@ public function fetchAssociative() return $row; } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } @@ -92,7 +79,13 @@ public function fetchFirstColumn(): array public function rowCount(): int { - return @db2_num_rows($this->statement); + $numRows = @db2_num_rows($this->statement); + + if ($numRows === false) { + throw StatementError::new($this->statement); + } + + return $numRows; } public function columnCount(): int diff --git a/doctrine/dbal/src/Driver/IBMDB2/Statement.php b/doctrine/dbal/src/Driver/IBMDB2/Statement.php index 699e236d7..96852dae4 100644 --- a/doctrine/dbal/src/Driver/IBMDB2/Statement.php +++ b/doctrine/dbal/src/Driver/IBMDB2/Statement.php @@ -1,22 +1,21 @@ stmt = $stmt; } - /** - * {@inheritDoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { assert(is_int($param)); - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->bindParam($param, $value, $type); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - assert(is_int($param)); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - switch ($type) { case ParameterType::INTEGER: - $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); + $this->bind($param, $value, DB2_PARAM_IN, DB2_LONG); break; case ParameterType::LARGE_OBJECT: - $this->lobs[$param] = &$variable; + $this->lobs[$param] = &$value; break; default: - $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR); + $this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR); break; } - - return true; } - /** - * @param int $position Parameter position - * @param mixed $variable - * - * @throws Exception - */ - private function bind($position, &$variable, int $parameterType, int $dataType): void + /** @throws Exception */ + private function bind(int $position, mixed &$variable, int $parameterType, int $dataType): void { $this->parameters[$position] =& $variable; @@ -131,23 +79,11 @@ private function bind($position, &$variable, int $parameterType, int $dataType): } } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - } - $handles = $this->bindLobs(); - $result = @db2_execute($this->stmt, $params ?? $this->parameters); + $result = @db2_execute($this->stmt, $this->parameters); foreach ($handles as $handle) { fclose($handle); diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php index f2809cd0a..bdf9b2f84 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractConnectionMiddleware.php @@ -1,26 +1,17 @@ wrappedConnection = $wrappedConnection; } public function prepare(string $sql): Statement @@ -33,81 +24,46 @@ public function query(string $sql): Result return $this->wrappedConnection->query($sql); } - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - return $this->wrappedConnection->quote($value, $type); + return $this->wrappedConnection->quote($value); } - public function exec(string $sql): int + public function exec(string $sql): int|string { return $this->wrappedConnection->exec($sql); } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - } - - return $this->wrappedConnection->lastInsertId($name); + return $this->wrappedConnection->lastInsertId(); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): void { - return $this->wrappedConnection->beginTransaction(); + $this->wrappedConnection->beginTransaction(); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { - return $this->wrappedConnection->commit(); + $this->wrappedConnection->commit(); } - /** - * {@inheritDoc} - */ - public function rollBack() + public function rollBack(): void { - return $this->wrappedConnection->rollBack(); + $this->wrappedConnection->rollBack(); } - /** - * {@inheritDoc} - */ - public function getServerVersion() + public function getServerVersion(): string { - if (! $this->wrappedConnection instanceof ServerInfoAwareConnection) { - throw new LogicException('The underlying connection is not a ServerInfoAwareConnection'); - } - return $this->wrappedConnection->getServerVersion(); } - /** @return resource|object */ + /** + * {@inheritDoc} + */ public function getNativeConnection() { - if (! method_exists($this->wrappedConnection, 'getNativeConnection')) { - throw new LogicException(sprintf( - 'The driver connection %s does not support accessing the native connection.', - get_class($this->wrappedConnection), - )); - } - return $this->wrappedConnection->getNativeConnection(); } } diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php index 1c9d43097..482f13458 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractDriverMiddleware.php @@ -1,22 +1,20 @@ wrappedDriver = $wrappedDriver; } /** @@ -24,50 +22,18 @@ public function __construct(Driver $wrappedDriver) */ public function connect( #[SensitiveParameter] - array $params - ) { + array $params, + ): DriverConnection { return $this->wrappedDriver->connect($params); } - /** - * {@inheritDoc} - */ - public function getDatabasePlatform() + public function getDatabasePlatform(ServerVersionProvider $versionProvider): AbstractPlatform { - return $this->wrappedDriver->getDatabasePlatform(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead. - */ - public function getSchemaManager(Connection $conn, AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5458', - 'AbstractDriverMiddleware::getSchemaManager() is deprecated.' - . ' Use AbstractPlatform::createSchemaManager() instead.', - ); - - return $this->wrappedDriver->getSchemaManager($conn, $platform); + return $this->wrappedDriver->getDatabasePlatform($versionProvider); } public function getExceptionConverter(): ExceptionConverter { return $this->wrappedDriver->getExceptionConverter(); } - - /** - * {@inheritDoc} - */ - public function createDatabasePlatformForVersion($version) - { - if ($this->wrappedDriver instanceof VersionAwarePlatformDriver) { - return $this->wrappedDriver->createDatabasePlatformForVersion($version); - } - - return $this->wrappedDriver->getDatabasePlatform(); - } } diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php index 198d39b0d..7da2f9974 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractResultMiddleware.php @@ -1,38 +1,28 @@ wrappedResult = $result; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->wrappedResult->fetchNumeric(); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->wrappedResult->fetchAssociative(); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return $this->wrappedResult->fetchOne(); } @@ -61,7 +51,7 @@ public function fetchFirstColumn(): array return $this->wrappedResult->fetchFirstColumn(); } - public function rowCount(): int + public function rowCount(): int|string { return $this->wrappedResult->rowCount(); } diff --git a/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php index 6cd2f8f08..6eaad509f 100644 --- a/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php +++ b/doctrine/dbal/src/Driver/Middleware/AbstractStatementMiddleware.php @@ -1,71 +1,26 @@ wrappedStatement = $wrappedStatement; } - /** - * {@inheritDoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->wrappedStatement->bindValue($param, $value, $type); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->wrappedStatement->bindParam($param, $variable, $type, $length); + $this->wrappedStatement->bindValue($param, $value, $type); } - /** - * {@inheritDoc} - */ - public function execute($params = null): Result + public function execute(): Result { - return $this->wrappedStatement->execute($params); + return $this->wrappedStatement->execute(); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Connection.php b/doctrine/dbal/src/Driver/Mysqli/Connection.php index d492684cc..cc00fb62c 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Connection.php +++ b/doctrine/dbal/src/Driver/Mysqli/Connection.php @@ -1,48 +1,25 @@ connection = $connection; - } - - /** - * Retrieves mysqli native resource handle. - * - * Could be used if part of your application is not using DBAL. - * - * @deprecated Call {@see getNativeConnection()} instead. - */ - public function getWrappedResourceHandle(): mysqli + public function __construct(private readonly mysqli $connection) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5037', - '%s is deprecated, call getNativeConnection() instead.', - __METHOD__, - ); - - return $this->getNativeConnection(); } public function getServerVersion(): string @@ -50,7 +27,7 @@ public function getServerVersion(): string return $this->connection->get_server_info(); } - public function prepare(string $sql): DriverStatement + public function prepare(string $sql): Statement { try { $stmt = $this->connection->prepare($sql); @@ -65,20 +42,17 @@ public function prepare(string $sql): DriverStatement return new Statement($stmt); } - public function query(string $sql): ResultInterface + public function query(string $sql): Result { return $this->prepare($sql)->execute(); } - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { return "'" . $this->connection->escape_string($value) . "'"; } - public function exec(string $sql): int + public function exec(string $sql): int|string { try { $result = $this->connection->query($sql); @@ -93,44 +67,41 @@ public function exec(string $sql): int return $this->connection->affected_rows; } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); + $lastInsertId = $this->connection->insert_id; + + if ($lastInsertId === 0) { + throw Exception\NoIdentityValue::new(); } return $this->connection->insert_id; } - public function beginTransaction(): bool + public function beginTransaction(): void { $this->connection->begin_transaction(); - - return true; } - public function commit(): bool + public function commit(): void { try { - return $this->connection->commit(); + if (! $this->connection->commit()) { + throw ConnectionError::new($this->connection); + } } catch (mysqli_sql_exception $e) { - return false; + throw ConnectionError::upcast($e); } } - public function rollBack(): bool + public function rollBack(): void { try { - return $this->connection->rollback(); + if (! $this->connection->rollback()) { + throw ConnectionError::new($this->connection); + } } catch (mysqli_sql_exception $e) { - return false; + throw ConnectionError::upcast($e); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Driver.php b/doctrine/dbal/src/Driver/Mysqli/Driver.php index 4f5186875..9855e565d 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Driver.php +++ b/doctrine/dbal/src/Driver/Mysqli/Driver.php @@ -1,5 +1,7 @@ real_connect( $host, - $params['user'] ?? null, - $params['password'] ?? null, - $params['dbname'] ?? null, - $params['port'] ?? null, - $params['unix_socket'] ?? null, + $params['user'] ?? '', + $params['password'] ?? '', + $params['dbname'] ?? '', + $params['port'] ?? 0, + $params['unix_socket'] ?? '', $params['driverOptions'][Connection::OPTION_FLAGS] ?? 0, ); } catch (mysqli_sql_exception $e) { @@ -72,7 +72,7 @@ public function connect( */ private function compilePreInitializers( #[SensitiveParameter] - array $params + array $params, ): Generator { unset($params['driverOptions'][Connection::OPTION_FLAGS]); @@ -106,7 +106,7 @@ private function compilePreInitializers( */ private function compilePostInitializers( #[SensitiveParameter] - array $params + array $params, ): Generator { if (! isset($params['charset'])) { return; diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php index ef5b98017..d2477fdba 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionError.php @@ -24,8 +24,7 @@ public static function new(mysqli $connection): self public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); - $p->setAccessible(true); - return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); + return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php index 44a8cab99..cb3bc64f7 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/ConnectionFailed.php @@ -29,8 +29,7 @@ public static function new(mysqli $connection): self public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); - $p->setAccessible(true); - return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); + return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php index 8c6bbb476..778ea6451 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidCharset.php @@ -30,12 +30,11 @@ public static function fromCharset(mysqli $connection, string $charset): self public static function upcast(mysqli_sql_exception $exception, string $charset): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); - $p->setAccessible(true); return new self( sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()), $p->getValue($exception), - (int) $exception->getCode(), + $exception->getCode(), $exception, ); } diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php index 6fb46316e..654bb87b2 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/InvalidOption.php @@ -15,8 +15,7 @@ */ final class InvalidOption extends AbstractException { - /** @param mixed $value */ - public static function fromOption(int $option, $value): self + public static function fromOption(int $option, mixed $value): self { return new self( sprintf('Failed to set option %d with value "%s"', $option, $value), diff --git a/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php b/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php index 78dc8556b..991384cd9 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php +++ b/doctrine/dbal/src/Driver/Mysqli/Exception/StatementError.php @@ -24,8 +24,7 @@ public static function new(mysqli_stmt $statement): self public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); - $p->setAccessible(true); - return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); + return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception); } } diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php index 8143a265c..d02c76841 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Charset.php @@ -11,11 +11,8 @@ final class Charset implements Initializer { - private string $charset; - - public function __construct(string $charset) + public function __construct(private readonly string $charset) { - $this->charset = $charset; } public function initialize(mysqli $connection): void diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php index 2e66f8d69..322395135 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Options.php @@ -12,13 +12,9 @@ final class Options implements Initializer { - /** @var array */ - private array $options; - /** @param array $options */ - public function __construct(array $options) + public function __construct(private readonly array $options) { - $this->options = $options; } public function initialize(mysqli $connection): void diff --git a/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php b/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php index a25fcfc2d..fa819b5a2 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php +++ b/doctrine/dbal/src/Driver/Mysqli/Initializer/Secure.php @@ -10,25 +10,14 @@ final class Secure implements Initializer { - private string $key; - private string $cert; - private string $ca; - private string $capath; - private string $cipher; - public function __construct( #[SensitiveParameter] - string $key, - string $cert, - string $ca, - string $capath, - string $cipher + private readonly string $key, + private readonly string $cert, + private readonly string $ca, + private readonly string $capath, + private readonly string $cipher, ) { - $this->key = $key; - $this->cert = $cert; - $this->ca = $ca; - $this->capath = $capath; - $this->cipher = $cipher; } public function initialize(mysqli $connection): void diff --git a/doctrine/dbal/src/Driver/Mysqli/Result.php b/doctrine/dbal/src/Driver/Mysqli/Result.php index c7dc65d1d..8d3c47af5 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Result.php +++ b/doctrine/dbal/src/Driver/Mysqli/Result.php @@ -18,13 +18,11 @@ final class Result implements ResultInterface { - private mysqli_stmt $statement; - /** * Whether the statement result has columns. The property should be used only after the result metadata * has been fetched ({@see $metadataFetched}). Otherwise, the property value is undetermined. */ - private bool $hasColumns = false; + private readonly bool $hasColumns; /** * Mapping of statement result column indexes to their names. The property should be used only @@ -32,7 +30,7 @@ final class Result implements ResultInterface * * @var array */ - private array $columnNames = []; + private readonly array $columnNames; /** @var mixed[] */ private array $boundValues = []; @@ -42,20 +40,16 @@ final class Result implements ResultInterface * * @throws Exception */ - public function __construct(mysqli_stmt $statement) + public function __construct(private readonly mysqli_stmt $statement) { - $this->statement = $statement; - - $meta = $statement->result_metadata(); + $meta = $statement->result_metadata(); + $this->hasColumns = $meta !== false; + $this->columnNames = $meta !== false ? array_column($meta->fetch_fields(), 'name') : []; if ($meta === false) { return; } - $this->hasColumns = true; - - $this->columnNames = array_column($meta->fetch_fields(), 'name'); - $meta->free(); // Store result of every execution which has it. Otherwise it will be impossible @@ -84,10 +78,7 @@ public function __construct(mysqli_stmt $statement) } } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { try { $ret = $this->statement->fetch(); @@ -112,10 +103,7 @@ public function fetchNumeric() return $values; } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { $values = $this->fetchNumeric(); @@ -126,10 +114,7 @@ public function fetchAssociative() return array_combine($this->columnNames, $values); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } @@ -158,7 +143,7 @@ public function fetchFirstColumn(): array return FetchUtils::fetchFirstColumn($this); } - public function rowCount(): int + public function rowCount(): int|string { if ($this->hasColumns) { return $this->statement->num_rows; diff --git a/doctrine/dbal/src/Driver/Mysqli/Statement.php b/doctrine/dbal/src/Driver/Mysqli/Statement.php index fec7c95c3..8436fadfc 100644 --- a/doctrine/dbal/src/Driver/Mysqli/Statement.php +++ b/doctrine/dbal/src/Driver/Mysqli/Statement.php @@ -1,16 +1,15 @@ 's', - ParameterType::STRING => 's', - ParameterType::BINARY => 's', - ParameterType::BOOLEAN => 'i', - ParameterType::NULL => 's', - ParameterType::INTEGER => 'i', - ParameterType::LARGE_OBJECT => 'b', - ]; - - private mysqli_stmt $stmt; + private const PARAMETER_TYPE_STRING = 's'; + private const PARAMETER_TYPE_INTEGER = 'i'; + private const PARAMETER_TYPE_BINARY = 'b'; /** @var mixed[] */ private array $boundValues; @@ -52,113 +42,36 @@ final class Statement implements StatementInterface private array $values = []; /** @internal The statement can be only instantiated by its driver connection. */ - public function __construct(mysqli_stmt $stmt) + public function __construct(private readonly mysqli_stmt $stmt) { - $this->stmt = $stmt; - $paramCount = $this->stmt->param_count; - $this->types = str_repeat('s', $paramCount); + $this->types = str_repeat(self::PARAMETER_TYPE_STRING, $paramCount); $this->boundValues = array_fill(1, $paramCount, null); } - /** - * @deprecated Use {@see bindValue()} instead. - * - * {@inheritDoc} - * - * @psalm-assert ParameterType::* $type - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - assert(is_int($param)); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - if (! isset(self::PARAM_TYPE_MAP[$type])) { - throw UnknownParameterType::new($type); - } - - $this->boundValues[$param] =& $variable; - $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; - - return true; - } - - /** - * {@inheritDoc} - * - * @psalm-assert ParameterType::* $type - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { assert(is_int($param)); - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - if (! isset(self::PARAM_TYPE_MAP[$type])) { - throw UnknownParameterType::new($type); - } - + $this->types[$param - 1] = $this->convertParameterType($type); $this->values[$param] = $value; $this->boundValues[$param] =& $this->values[$param]; - $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; - - return true; } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); + if (count($this->boundValues) > 0) { + $this->bindParameters(); } - if ($params !== null && count($params) > 0) { - if (! $this->bindUntypedValues($params)) { + try { + if (! $this->stmt->execute()) { throw StatementError::new($this->stmt); } - } elseif (count($this->boundValues) > 0) { - $this->bindTypedParameters(); - } - - try { - $result = $this->stmt->execute(); } catch (mysqli_sql_exception $e) { throw StatementError::upcast($e); } - if (! $result) { - throw StatementError::new($this->stmt); - } - return new Result($this->stmt); } @@ -167,19 +80,18 @@ public function execute($params = null): ResultInterface * * @throws Exception */ - private function bindTypedParameters(): void + private function bindParameters(): void { $streams = $values = []; $types = $this->types; foreach ($this->boundValues as $parameter => $value) { assert(is_int($parameter)); - if (! isset($types[$parameter - 1])) { - $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; + $types[$parameter - 1] = self::PARAMETER_TYPE_STRING; } - if ($types[$parameter - 1] === self::PARAM_TYPE_MAP[ParameterType::LARGE_OBJECT]) { + if ($types[$parameter - 1] === self::PARAMETER_TYPE_BINARY) { if (is_resource($value)) { if (get_resource_type($value) !== 'stream') { throw NonStreamResourceUsedAsLargeObject::new($parameter); @@ -190,7 +102,7 @@ private function bindTypedParameters(): void continue; } - $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; + $types[$parameter - 1] = self::PARAMETER_TYPE_STRING; } $values[$parameter] = $value; @@ -227,13 +139,16 @@ private function sendLongData(array $streams): void } } - /** - * Binds a array of values to bound parameters. - * - * @param mixed[] $values - */ - private function bindUntypedValues(array $values): bool + private function convertParameterType(ParameterType $type): string { - return $this->stmt->bind_param(str_repeat('s', count($values)), ...$values); + return match ($type) { + ParameterType::NULL, + ParameterType::STRING, + ParameterType::ASCII, + ParameterType::BINARY => self::PARAMETER_TYPE_STRING, + ParameterType::INTEGER, + ParameterType::BOOLEAN => self::PARAMETER_TYPE_INTEGER, + ParameterType::LARGE_OBJECT => self::PARAMETER_TYPE_BINARY, + }; } } diff --git a/doctrine/dbal/src/Driver/OCI8/Connection.php b/doctrine/dbal/src/Driver/OCI8/Connection.php index 72353fa31..3652ca0eb 100644 --- a/doctrine/dbal/src/Driver/OCI8/Connection.php +++ b/doctrine/dbal/src/Driver/OCI8/Connection.php @@ -1,21 +1,17 @@ connection = $connection; $this->parser = new Parser(false); $this->executionMode = new ExecutionMode(); } @@ -47,10 +39,7 @@ public function __construct($connection) public function getServerVersion(): string { $version = oci_server_version($this->connection); - - if ($version === false) { - throw Error::new($this->connection); - } + assert($version !== false); $result = preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches); assert($result === 1); @@ -59,7 +48,7 @@ public function getServerVersion(): string } /** @throws Parser\Exception */ - public function prepare(string $sql): DriverStatement + public function prepare(string $sql): Statement { $visitor = new ConvertPositionalToNamedPlaceholders(); @@ -75,91 +64,51 @@ public function prepare(string $sql): DriverStatement * @throws Exception * @throws Parser\Exception */ - public function query(string $sql): ResultInterface + public function query(string $sql): Result { return $this->prepare($sql)->execute(); } - /** - * {@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") . "'"; + return "'" . addcslashes(str_replace("'", "''", $value), "\000\n\r\\\032") . "'"; } /** * @throws Exception * @throws Parser\Exception */ - public function exec(string $sql): int + public function exec(string $sql): int|string { return $this->prepare($sql)->execute()->rowCount(); } - /** - * {@inheritDoc} - * - * @param string|null $name - * - * @return int|false - * - * @throws Parser\Exception - */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name === null) { - return false; - } - - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - - $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); - - if ($result === false) { - throw SequenceDoesNotExist::new(); - } - - return (int) $result; + throw IdentityColumnsNotSupported::new(); } - public function beginTransaction(): bool + public function beginTransaction(): void { $this->executionMode->disableAutoCommit(); - - return true; } - public function commit(): bool + public function commit(): void { if (! oci_commit($this->connection)) { throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); - - return true; } - public function rollBack(): bool + public function rollBack(): void { if (! oci_rollback($this->connection)) { throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); - - return true; } /** @return resource */ diff --git a/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php b/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php index e2a112629..5898a2c5e 100644 --- a/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php +++ b/doctrine/dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.php @@ -1,5 +1,7 @@ getEasyConnectString($params); + /** @psalm-suppress RiskyTruthyFalsyComparison */ $persistent = ! empty($params['persistent']); - $exclusive = ! empty($params['driverOptions']['exclusive']); + /** @psalm-suppress RiskyTruthyFalsyComparison */ + $exclusive = ! empty($params['driverOptions']['exclusive']); if ($persistent && $exclusive) { throw InvalidConfiguration::forPersistentAndExclusive(); diff --git a/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php b/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php deleted file mode 100644 index 5fa43caba..000000000 --- a/doctrine/dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.php +++ /dev/null @@ -1,20 +0,0 @@ -statement = $statement; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->fetch(OCI_NUM); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->fetch(OCI_ASSOC); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } @@ -113,12 +100,8 @@ public function free(): void oci_cancel($this->statement); } - /** - * @return mixed|false - * - * @throws Exception - */ - private function fetch(int $mode) + /** @throws Exception */ + private function fetch(int $mode): mixed { $result = oci_fetch_array($this->statement, $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS); diff --git a/doctrine/dbal/src/Driver/OCI8/Statement.php b/doctrine/dbal/src/Driver/OCI8/Statement.php index 015a14b7b..408f0dd5d 100644 --- a/doctrine/dbal/src/Driver/OCI8/Statement.php +++ b/doctrine/dbal/src/Driver/OCI8/Statement.php @@ -1,15 +1,14 @@ */ - private array $parameterMap; - - private ExecutionMode $executionMode; - /** * @internal The statement can be only instantiated by its driver connection. * @@ -43,54 +31,16 @@ final class Statement implements StatementInterface * @param resource $statement * @param array $parameterMap */ - public function __construct($connection, $statement, array $parameterMap, ExecutionMode $executionMode) - { - $this->connection = $connection; - $this->statement = $statement; - $this->parameterMap = $parameterMap; - $this->executionMode = $executionMode; + public function __construct( + private readonly mixed $connection, + private readonly mixed $statement, + private readonly array $parameterMap, + private readonly ExecutionMode $executionMode, + ) { } - /** - * {@inheritDoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool - { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->bindParam($param, $value, $type); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - if (is_int($param)) { if (! isset($this->parameterMap[$param])) { throw UnknownParameterIndex::new($param); @@ -100,64 +50,43 @@ public function bindParam($param, &$variable, $type = ParameterType::STRING, $le } if ($type === ParameterType::LARGE_OBJECT) { - if ($variable !== null) { + if ($value !== null) { $lob = oci_new_descriptor($this->connection, OCI_D_LOB); - $lob->writeTemporary($variable, OCI_TEMP_BLOB); + $lob->writeTemporary($value, OCI_TEMP_BLOB); - $variable =& $lob; + $value =& $lob; } else { $type = ParameterType::STRING; } } - return oci_bind_by_name( - $this->statement, - $param, - $variable, - $length ?? -1, - $this->convertParameterType($type), - ); + if ( + ! @oci_bind_by_name( + $this->statement, + $param, + $value, + -1, + $this->convertParameterType($type), + ) + ) { + throw Error::new($this->statement); + } } /** * Converts DBAL parameter type to oci8 parameter type */ - private function convertParameterType(int $type): int + private function convertParameterType(ParameterType $type): int { - switch ($type) { - case ParameterType::BINARY: - return OCI_B_BIN; - - case ParameterType::LARGE_OBJECT: - return OCI_B_BLOB; - - default: - return SQLT_CHR; - } + return match ($type) { + ParameterType::BINARY => OCI_B_BIN, + ParameterType::LARGE_OBJECT => OCI_B_BLOB, + default => SQLT_CHR, + }; } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - - foreach ($params as $key => $val) { - if (is_int($key)) { - $this->bindValue($key + 1, $val, ParameterType::STRING); - } else { - $this->bindValue($key, $val, ParameterType::STRING); - } - } - } - if ($this->executionMode->isAutoCommitEnabled()) { $mode = OCI_COMMIT_ON_SUCCESS; } else { diff --git a/doctrine/dbal/src/Driver/PDO/Connection.php b/doctrine/dbal/src/Driver/PDO/Connection.php index 290dcc2d2..b1faca147 100644 --- a/doctrine/dbal/src/Driver/PDO/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/Connection.php @@ -1,30 +1,24 @@ setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - - $this->connection = $connection; } public function exec(string $sql): int @@ -40,20 +34,12 @@ public function exec(string $sql): int } } - /** - * {@inheritDoc} - */ - public function getServerVersion() + public function getServerVersion(): string { return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); } - /** - * {@inheritDoc} - * - * @return Statement - */ - public function prepare(string $sql): StatementInterface + public function prepare(string $sql): Statement { try { $stmt = $this->connection->prepare($sql); @@ -65,7 +51,7 @@ public function prepare(string $sql): StatementInterface } } - public function query(string $sql): ResultInterface + public function query(string $sql): Result { try { $stmt = $this->connection->query($sql); @@ -77,64 +63,66 @@ public function query(string $sql): ResultInterface } } - /** - * {@inheritDoc} - * - * @throws UnknownParameterType - * - * @psalm-assert ParameterType::* $type - */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - return $this->connection->quote($value, ParameterTypeMap::convertParamType($type)); + return $this->connection->quote($value); } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { try { - if ($name === null) { - return $this->connection->lastInsertId(); + $value = $this->connection->lastInsertId(); + } catch (PDOException $exception) { + assert($exception->errorInfo !== null); + [$sqlState] = $exception->errorInfo; + + // if the PDO driver does not support this capability, PDO::lastInsertId() triggers an IM001 SQLSTATE + // see https://www.php.net/manual/en/pdo.lastinsertid.php + if ($sqlState === 'IM001') { + throw IdentityColumnsNotSupported::new(); } - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); + // PDO PGSQL throws a 'lastval is not yet defined in this session' error when no identity value is + // available, with SQLSTATE 55000 'Object Not In Prerequisite State' + if ($sqlState === '55000' && $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) === 'pgsql') { + throw NoIdentityValue::new($exception); + } - return $this->connection->lastInsertId($name); - } catch (PDOException $exception) { throw Exception::new($exception); } + + // pdo_mysql & pdo_sqlite return '0', pdo_sqlsrv returns '' or false depending on the PHP version + if ($value === '0' || $value === '' || $value === false) { + throw NoIdentityValue::new(); + } + + return $value; } - public function beginTransaction(): bool + public function beginTransaction(): void { try { - return $this->connection->beginTransaction(); + $this->connection->beginTransaction(); } catch (PDOException $exception) { - throw DriverPDOException::new($exception); + throw Exception::new($exception); } } - public function commit(): bool + public function commit(): void { try { - return $this->connection->commit(); + $this->connection->commit(); } catch (PDOException $exception) { - throw DriverPDOException::new($exception); + throw Exception::new($exception); } } - public function rollBack(): bool + public function rollBack(): void { try { - return $this->connection->rollBack(); + $this->connection->rollBack(); } catch (PDOException $exception) { - throw DriverPDOException::new($exception); + throw Exception::new($exception); } } @@ -142,17 +130,4 @@ public function getNativeConnection(): PDO { return $this->connection; } - - /** @deprecated Call {@see getNativeConnection()} instead. */ - public function getWrappedConnection(): PDO - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5037', - '%s is deprecated, call getNativeConnection() instead.', - __METHOD__, - ); - - return $this->getNativeConnection(); - } } diff --git a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php index 2492698b4..963fef04b 100644 --- a/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/MySQL/Driver.php @@ -1,5 +1,7 @@ message, 0, $previous); - - $exception->errorInfo = $previous->errorInfo; - $exception->code = $previous->code; - $exception->sqlState = $previous->errorInfo[0] ?? null; - - return $exception; - } - - public function getSQLState(): ?string - { - return $this->sqlState; - } -} diff --git a/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php b/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php deleted file mode 100644 index f17b585f7..000000000 --- a/doctrine/dbal/src/Driver/PDO/ParameterTypeMap.php +++ /dev/null @@ -1,49 +0,0 @@ - PDO::PARAM_NULL, - ParameterType::INTEGER => PDO::PARAM_INT, - ParameterType::STRING => PDO::PARAM_STR, - ParameterType::ASCII => PDO::PARAM_STR, - ParameterType::BINARY => PDO::PARAM_LOB, - ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, - ParameterType::BOOLEAN => PDO::PARAM_BOOL, - ]; - - /** - * Converts DBAL parameter type to PDO parameter type - * - * @psalm-return PDO::PARAM_* - * - * @throws UnknownParameterType - * - * @psalm-assert ParameterType::* $type - */ - public static function convertParamType(int $type): int - { - if (! isset(self::PARAM_TYPE_MAP[$type])) { - throw UnknownParameterType::new($type); - } - - return self::PARAM_TYPE_MAP[$type]; - } - - private function __construct() - { - } - - private function __clone() - { - } -} diff --git a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php index 5bfcd730c..a267e8407 100644 --- a/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/PgSQL/Driver.php @@ -1,11 +1,12 @@ statement = $statement; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->fetch(PDO::FETCH_NUM); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->fetch(PDO::FETCH_ASSOC); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return $this->fetch(PDO::FETCH_COLUMN); } @@ -93,11 +81,9 @@ public function free(): void /** * @psalm-param PDO::FETCH_* $mode * - * @return mixed - * * @throws Exception */ - private function fetch(int $mode) + private function fetch(int $mode): mixed { try { return $this->statement->fetch($mode); diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php index 9015f5558..78ba7f821 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Connection.php @@ -1,70 +1,29 @@ connection = $connection; } - public function prepare(string $sql): StatementInterface + public function prepare(string $sql): Statement { return new Statement( $this->connection->prepare($sql), ); } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) - { - if ($name === null) { - return parent::lastInsertId($name); - } - - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - - $statement = $this->prepare( - 'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?', - ); - $statement->bindValue(1, $name); - - return $statement->execute() - ->fetchOne(); - } - public function getNativeConnection(): PDO { return $this->connection->getNativeConnection(); } - - /** @deprecated Call {@see getNativeConnection()} instead. */ - public function getWrappedConnection(): PDO - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5037', - '%s is deprecated, call getNativeConnection() instead.', - __METHOD__, - ); - - return $this->connection->getWrappedConnection(); - } } diff --git a/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php b/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php index 63eabb71a..a3cae9785 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLSrv/Driver.php @@ -1,5 +1,7 @@ statement = $statement; } - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - * - * @param string|int $param - * @param mixed $variable - * @param int $type - * @param int|null $length - * @param mixed $driverOptions The usage of the argument is deprecated. - * - * @throws UnknownParameterType - * - * @psalm-assert ParameterType::* $type - */ - public function bindParam( - $param, - &$variable, - $type = ParameterType::STRING, - $length = null, - $driverOptions = null - ): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - if (func_num_args() > 4) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4533', - 'The $driverOptions argument of Statement::bindParam() is deprecated.', - ); - } - + public function bindValue(int|string $param, mixed $value, ParameterType $type): void + { switch ($type) { case ParameterType::LARGE_OBJECT: case ParameterType::BINARY: - $driverOptions ??= PDO::SQLSRV_ENCODING_BINARY; - + $this->statement->bindParamWithDriverOptions( + $param, + $value, + $type, + PDO::SQLSRV_ENCODING_BINARY, + ); break; case ParameterType::ASCII: - $type = ParameterType::STRING; - $length = 0; - $driverOptions = PDO::SQLSRV_ENCODING_SYSTEM; + $this->statement->bindParamWithDriverOptions( + $param, + $value, + ParameterType::STRING, + PDO::SQLSRV_ENCODING_SYSTEM, + ); break; - } - return $this->statement->bindParam($param, $variable, $type, $length ?? 0, $driverOptions); - } - - /** - * @throws UnknownParameterType - * - * {@inheritDoc} - * - * @psalm-assert ParameterType::* $type - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool - { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); + default: + $this->statement->bindValue($param, $value, $type); } - - return $this->bindParam($param, $value, $type); } } diff --git a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php index 2e97788e6..74194a59c 100644 --- a/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php +++ b/doctrine/dbal/src/Driver/PDO/SQLite/Driver.php @@ -1,12 +1,12 @@ constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])), $params['user'] ?? '', $params['password'] ?? '', - $driverOptions, + $params['driverOptions'] ?? [], ); } catch (PDOException $exception) { throw Exception::new($exception); } - UserDefinedFunctions::register( - [$pdo, 'sqliteCreateFunction'], - $userDefinedFunctions, - ); - return new Connection($pdo); } diff --git a/doctrine/dbal/src/Driver/PDO/Statement.php b/doctrine/dbal/src/Driver/PDO/Statement.php index 64f318d2c..4f0e070b7 100644 --- a/doctrine/dbal/src/Driver/PDO/Statement.php +++ b/doctrine/dbal/src/Driver/PDO/Statement.php @@ -1,137 +1,80 @@ stmt = $stmt; } - /** - * {@inheritDoc} - * - * @throws UnknownParameterType - * - * @psalm-assert ParameterType::* $type - */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - $pdoType = ParameterTypeMap::convertParamType($type); + $pdoType = $this->convertParamType($type); try { - return $this->stmt->bindValue($param, $value, $pdoType); + $this->stmt->bindValue($param, $value, $pdoType); } catch (PDOException $exception) { throw Exception::new($exception); } } /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - * - * @param mixed $param - * @param mixed $variable - * @param int $type - * @param int|null $length - * @param mixed $driverOptions The usage of the argument is deprecated. + * @internal Driver options can be only specified by a PDO-based driver. * - * @throws UnknownParameterType - * - * @psalm-assert ParameterType::* $type + * @throws ExceptionInterface */ - public function bindParam( - $param, - &$variable, - $type = ParameterType::STRING, - $length = null, - $driverOptions = null - ): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - if (func_num_args() > 4) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4533', - 'The $driverOptions argument of Statement::bindParam() is deprecated.', - ); - } - - $pdoType = ParameterTypeMap::convertParamType($type); + public function bindParamWithDriverOptions( + string|int $param, + mixed &$variable, + ParameterType $type, + mixed $driverOptions, + ): void { + $pdoType = $this->convertParamType($type); try { - return $this->stmt->bindParam( - $param, - $variable, - $pdoType, - $length ?? 0, - ...array_slice(func_get_args(), 4), - ); + $this->stmt->bindParam($param, $variable, $pdoType, 0, $driverOptions); } catch (PDOException $exception) { throw Exception::new($exception); } } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - } - try { - $this->stmt->execute($params); + $this->stmt->execute(); } catch (PDOException $exception) { throw Exception::new($exception); } return new Result($this->stmt); } + + /** + * Converts DBAL parameter type to PDO parameter type + * + * @psalm-return PDO::PARAM_* + */ + private function convertParamType(ParameterType $type): int + { + return match ($type) { + ParameterType::NULL => PDO::PARAM_NULL, + ParameterType::INTEGER => PDO::PARAM_INT, + ParameterType::STRING, + ParameterType::ASCII => PDO::PARAM_STR, + ParameterType::BINARY, + ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, + ParameterType::BOOLEAN => PDO::PARAM_BOOL, + }; + } } diff --git a/doctrine/dbal/src/Driver/PgSQL/Connection.php b/doctrine/dbal/src/Driver/PgSQL/Connection.php index 378e8ed7a..4f280b0dc 100644 --- a/doctrine/dbal/src/Driver/PgSQL/Connection.php +++ b/doctrine/dbal/src/Driver/PgSQL/Connection.php @@ -1,21 +1,16 @@ connection = $connection; - $this->parser = new Parser(false); + $this->parser = new Parser(false); } public function __destruct() @@ -94,13 +75,12 @@ public function query(string $sql): Result } /** {@inheritDoc} */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - if ($type === ParameterType::BINARY || $type === ParameterType::LARGE_OBJECT) { - return sprintf("'%s'", pg_escape_bytea($this->connection, $value)); - } + $quotedValue = pg_escape_literal($this->connection, $value); + assert($quotedValue !== false); - return pg_escape_literal($this->connection, $value); + return $quotedValue; } public function exec(string $sql): int @@ -109,43 +89,32 @@ public function exec(string $sql): int } /** {@inheritDoc} */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - - return $this->query(sprintf('SELECT CURRVAL(%s)', $this->quote($name)))->fetchOne(); + try { + return $this->query('SELECT LASTVAL()')->fetchOne(); + } catch (Exception $exception) { + if ($exception->getSQLState() === '55000') { + throw NoIdentityValue::new($exception); + } + + throw $exception; } - - return $this->query('SELECT LASTVAL()')->fetchOne(); } - /** @return true */ - public function beginTransaction(): bool + public function beginTransaction(): void { $this->exec('BEGIN'); - - return true; } - /** @return true */ - public function commit(): bool + public function commit(): void { $this->exec('COMMIT'); - - return true; } - /** @return true */ - public function rollBack(): bool + public function rollBack(): void { $this->exec('ROLLBACK'); - - return true; } public function getServerVersion(): string @@ -153,8 +122,7 @@ public function getServerVersion(): string return (string) pg_version($this->connection)['server']; } - /** @return PgSqlConnection|resource */ - public function getNativeConnection() + public function getNativeConnection(): PgSqlConnection { return $this->connection; } diff --git a/doctrine/dbal/src/Driver/PgSQL/Driver.php b/doctrine/dbal/src/Driver/PgSQL/Driver.php index 6377499a7..0d5d605f8 100644 --- a/doctrine/dbal/src/Driver/PgSQL/Driver.php +++ b/doctrine/dbal/src/Driver/PgSQL/Driver.php @@ -1,5 +1,7 @@ $params['sslmode'] ?? null, 'gssencmode' => $params['gssencmode'] ?? null, ], - static fn ($value) => $value !== '' && $value !== null, + static fn (int|string|null $value) => $value !== '' && $value !== null, ); return implode(' ', array_map( - static fn ($value, string $key) => sprintf("%s='%s'", $key, addslashes($value)), + static fn (int|string $value, string $key) => sprintf("%s='%s'", $key, addslashes((string) $value)), array_values($components), array_keys($components), )); diff --git a/doctrine/dbal/src/Driver/PgSQL/Exception.php b/doctrine/dbal/src/Driver/PgSQL/Exception.php index 41e0dff19..3036e556d 100644 --- a/doctrine/dbal/src/Driver/PgSQL/Exception.php +++ b/doctrine/dbal/src/Driver/PgSQL/Exception.php @@ -1,5 +1,7 @@ result = $result; } @@ -61,7 +47,7 @@ public function __destruct() } /** {@inheritDoc} */ - public function fetchNumeric() + public function fetchNumeric(): array|false { if ($this->result === null) { return false; @@ -76,7 +62,7 @@ public function fetchNumeric() } /** {@inheritDoc} */ - public function fetchAssociative() + public function fetchAssociative(): array|false { if ($this->result === null) { return false; @@ -91,7 +77,7 @@ public function fetchAssociative() } /** {@inheritDoc} */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } @@ -103,17 +89,11 @@ public function fetchAllNumeric(): array return []; } - $resultSet = pg_fetch_all($this->result, PGSQL_NUM); - // On PHP 7.4, pg_fetch_all() might return false for empty result sets. - if ($resultSet === false) { - return []; - } - $types = $this->fetchNumericColumnTypes(); return array_map( fn (array $row) => $this->mapNumericRow($row, $types), - $resultSet, + pg_fetch_all($this->result, PGSQL_NUM), ); } @@ -124,17 +104,11 @@ public function fetchAllAssociative(): array return []; } - $resultSet = pg_fetch_all($this->result, PGSQL_ASSOC); - // On PHP 7.4, pg_fetch_all() might return false for empty result sets. - if ($resultSet === false) { - return []; - } - $types = $this->fetchAssociativeColumnTypes(); return array_map( fn (array $row) => $this->mapAssociativeRow($row, $types), - $resultSet, + pg_fetch_all($this->result, PGSQL_ASSOC), ); } @@ -244,39 +218,23 @@ private function mapAssociativeRow(array $row, array $types): array return $mappedRow; } - /** @return string|int|float|bool|null */ - private function mapType(string $postgresType, ?string $value) + private function mapType(string $postgresType, ?string $value): string|int|float|bool|null { if ($value === null) { return null; } - switch ($postgresType) { - case 'bool': - switch ($value) { - case 't': - return true; - case 'f': - return false; - } - - throw UnexpectedValue::new($value, $postgresType); - - case 'bytea': - return hex2bin(substr($value, 2)); - - case 'float4': - case 'float8': - return (float) $value; - - case 'int2': - case 'int4': - return (int) $value; - - case 'int8': - return PHP_INT_SIZE >= 8 ? (int) $value : $value; - } - - return $value; + return match ($postgresType) { + 'bool' => match ($value) { + 't' => true, + 'f' => false, + default => throw UnexpectedValue::new($value, $postgresType), + }, + 'bytea' => hex2bin(substr($value, 2)), + 'float4', 'float8' => (float) $value, + 'int2', 'int4' => (int) $value, + 'int8' => PHP_INT_SIZE >= 8 ? (int) $value : $value, + default => $value, + }; } } diff --git a/doctrine/dbal/src/Driver/PgSQL/Statement.php b/doctrine/dbal/src/Driver/PgSQL/Statement.php index 75af66f30..b48ab5d83 100644 --- a/doctrine/dbal/src/Driver/PgSQL/Statement.php +++ b/doctrine/dbal/src/Driver/PgSQL/Statement.php @@ -1,20 +1,15 @@ */ - private array $parameterMap; - /** @var array */ private array $parameters = []; - /** @psalm-var array */ + /** @psalm-var array */ private array $parameterTypes = []; - /** - * @param PgSqlConnection|resource $connection - * @param array $parameterMap - */ - public function __construct($connection, string $name, array $parameterMap) - { - if (! is_resource($connection) && ! $connection instanceof PgSqlConnection) { - throw new TypeError(sprintf( - 'Expected connection to be a resource or an instance of %s, got %s.', - PgSqlConnection::class, - is_object($connection) ? get_class($connection) : gettype($connection), - )); - } - - $this->connection = $connection; - $this->name = $name; - $this->parameterMap = $parameterMap; + /** @param array $parameterMap */ + public function __construct( + private readonly PgSqlConnection $connection, + private readonly string $name, + private readonly array $parameterMap, + ) { } public function __destruct() @@ -75,7 +50,7 @@ public function __destruct() } /** {@inheritDoc} */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type = ParameterType::STRING): void { if (! isset($this->parameterMap[$param])) { throw UnknownParameter::new((string) $param); @@ -83,82 +58,21 @@ public function bindValue($param, $value, $type = ParameterType::STRING): bool $this->parameters[$this->parameterMap[$param]] = $value; $this->parameterTypes[$this->parameterMap[$param]] = $type; - - return true; - } - - /** {@inheritDoc} */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - if (func_num_args() > 4) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4533', - 'The $driverOptions argument of Statement::bindParam() is deprecated.', - ); - } - - if (! isset($this->parameterMap[$param])) { - throw UnknownParameter::new((string) $param); - } - - $this->parameters[$this->parameterMap[$param]] = &$variable; - $this->parameterTypes[$this->parameterMap[$param]] = $type; - - return true; } /** {@inheritDoc} */ - public function execute($params = null): Result + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - - foreach ($params as $param => $value) { - if (is_int($param)) { - $this->bindValue($param + 1, $value, ParameterType::STRING); - } else { - $this->bindValue($param, $value, ParameterType::STRING); - } - } - } - ksort($this->parameters); $escapedParameters = []; foreach ($this->parameters as $parameter => $value) { - switch ($this->parameterTypes[$parameter]) { - case ParameterType::BINARY: - case ParameterType::LARGE_OBJECT: - $escapedParameters[] = $value === null ? null : pg_escape_bytea( - $this->connection, - is_resource($value) ? stream_get_contents($value) : $value, - ); - break; - default: - $escapedParameters[] = $value; - } + $escapedParameters[] = match ($this->parameterTypes[$parameter]) { + ParameterType::BINARY, ParameterType::LARGE_OBJECT => $value === null + ? null + : pg_escape_bytea($this->connection, is_resource($value) ? stream_get_contents($value) : $value), + default => $value, + }; } if (@pg_send_execute($this->connection, $this->name, $escapedParameters) !== true) { diff --git a/doctrine/dbal/src/Driver/Result.php b/doctrine/dbal/src/Driver/Result.php index 7843a9589..500cb8870 100644 --- a/doctrine/dbal/src/Driver/Result.php +++ b/doctrine/dbal/src/Driver/Result.php @@ -16,7 +16,7 @@ interface Result * * @throws Exception */ - public function fetchNumeric(); + public function fetchNumeric(): array|false; /** * Returns the next row of the result as an associative array or FALSE if there are no more rows. @@ -25,16 +25,14 @@ public function fetchNumeric(); * * @throws Exception */ - public function fetchAssociative(); + public function fetchAssociative(): array|false; /** * Returns the first value of the next row of the result or FALSE if there are no more rows. * - * @return mixed|false - * * @throws Exception */ - public function fetchOne(); + public function fetchOne(): mixed; /** * Returns an array containing all of the result rows represented as numeric arrays. @@ -70,11 +68,13 @@ public function fetchFirstColumn(): array; * some database drivers may return the number of rows returned by that query. However, this behaviour * is not guaranteed for all drivers and should not be relied on in portable applications. * - * @return int The number of rows. + * If the number of rows exceeds {@see PHP_INT_MAX}, it might be returned as string if the driver supports it. + * + * @return int|numeric-string * * @throws Exception */ - public function rowCount(): int; + public function rowCount(): int|string; /** * Returns the number of columns in the result diff --git a/doctrine/dbal/src/Driver/SQLSrv/Connection.php b/doctrine/dbal/src/Driver/SQLSrv/Connection.php index 16e45d110..71050f17a 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Connection.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Connection.php @@ -1,17 +1,13 @@ connection = $connection; } - /** - * {@inheritDoc} - */ - public function getServerVersion() + public function getServerVersion(): string { $serverInfo = sqlsrv_server_info($this->connection); return $serverInfo['SQLServerVersion']; } - public function prepare(string $sql): DriverStatement + public function prepare(string $sql): Statement { return new Statement($this->connection, $sql); } - public function query(string $sql): ResultInterface + public function query(string $sql): Result { return $this->prepare($sql)->execute(); } - /** - * {@inheritDoc} - */ - public function quote($value, $type = ParameterType::STRING) + public function quote(string $value): string { - if (is_int($value)) { - return $value; - } - - if (is_float($value)) { - return sprintf('%F', $value); - } - return "'" . str_replace("'", "''", $value) . "'"; } @@ -88,52 +66,38 @@ public function exec(string $sql): int return $rowsAffected; } - /** - * {@inheritDoc} - */ - public function lastInsertId($name = null) + public function lastInsertId(): int|string { - if ($name !== null) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4687', - 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', - ); - - $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') - ->execute([$name]); - } else { - $result = $this->query('SELECT @@IDENTITY'); + $result = $this->query('SELECT @@IDENTITY'); + + $lastInsertId = $result->fetchOne(); + + if ($lastInsertId === null) { + throw NoIdentityValue::new(); } - return $result->fetchOne(); + return $lastInsertId; } - public function beginTransaction(): bool + public function beginTransaction(): void { if (! sqlsrv_begin_transaction($this->connection)) { throw Error::new(); } - - return true; } - public function commit(): bool + public function commit(): void { if (! sqlsrv_commit($this->connection)) { throw Error::new(); } - - return true; } - public function rollBack(): bool + public function rollBack(): void { if (! sqlsrv_rollback($this->connection)) { throw Error::new(); } - - return true; } /** @return resource */ diff --git a/doctrine/dbal/src/Driver/SQLSrv/Driver.php b/doctrine/dbal/src/Driver/SQLSrv/Driver.php index fcbdb7734..c9c2c3430 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Driver.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Driver.php @@ -1,5 +1,7 @@ statement = $stmt; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->fetch(SQLSRV_FETCH_NUMERIC); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->fetch(SQLSRV_FETCH_ASSOC); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } @@ -110,8 +97,7 @@ public function free(): void } } - /** @return mixed|false */ - private function fetch(int $fetchType) + private function fetch(int $fetchType): mixed { return sqlsrv_fetch_array($this->statement, $fetchType) ?? false; } diff --git a/doctrine/dbal/src/Driver/SQLSrv/Statement.php b/doctrine/dbal/src/Driver/SQLSrv/Statement.php index 227c33456..dc7827a50 100644 --- a/doctrine/dbal/src/Driver/SQLSrv/Statement.php +++ b/doctrine/dbal/src/Driver/SQLSrv/Statement.php @@ -1,16 +1,15 @@ + * @var array */ private array $types = []; @@ -67,13 +54,11 @@ final class Statement implements StatementInterface * @internal The statement can be only instantiated by its driver connection. * * @param resource $conn - * @param string $sql */ - public function __construct($conn, $sql) - { - $this->conn = $conn; - $this->sql = $sql; - + public function __construct( + private readonly mixed $conn, + private string $sql, + ) { if (stripos($sql, 'INSERT INTO ') !== 0) { return; } @@ -81,84 +66,16 @@ public function __construct($conn, $sql) $this->sql .= self::LAST_INSERT_ID_SQL; } - /** - * {@inheritDoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { assert(is_int($param)); - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - $this->variables[$param] = $value; $this->types[$param] = $type; - - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - assert(is_int($param)); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - $this->variables[$param] =& $variable; - $this->types[$param] = $type; - - // unset the statement resource if it exists as the new one will need to be bound to the new variable - $this->stmt = null; - - return true; } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - - foreach ($params as $key => $val) { - if (is_int($key)) { - $this->bindValue($key + 1, $val, ParameterType::STRING); - } else { - $this->bindValue($key, $val, ParameterType::STRING); - } - } - } - $this->stmt ??= $this->prepare(); if (! sqlsrv_execute($this->stmt)) { diff --git a/doctrine/dbal/src/Driver/SQLite3/Connection.php b/doctrine/dbal/src/Driver/SQLite3/Connection.php index 91b9b5ffe..1e9af93c9 100644 --- a/doctrine/dbal/src/Driver/SQLite3/Connection.php +++ b/doctrine/dbal/src/Driver/SQLite3/Connection.php @@ -1,22 +1,21 @@ connection = $connection; } public function prepare(string $sql): Statement @@ -45,8 +44,7 @@ public function query(string $sql): Result return new Result($result, $this->connection->changes()); } - /** @inheritDoc */ - public function quote($value, $type = ParameterType::STRING): string + public function quote(string $value): string { return sprintf('\'%s\'', SQLite3::escapeString($value)); } @@ -62,36 +60,40 @@ public function exec(string $sql): int return $this->connection->changes(); } - /** @inheritDoc */ - public function lastInsertId($name = null): int + public function lastInsertId(): int { - return $this->connection->lastInsertRowID(); + $value = $this->connection->lastInsertRowID(); + if ($value === 0) { + throw NoIdentityValue::new(); + } + + return $value; } - public function beginTransaction(): bool + public function beginTransaction(): void { try { - return $this->connection->exec('BEGIN'); + $this->connection->exec('BEGIN'); } catch (\Exception $e) { - return false; + throw Exception::new($e); } } - public function commit(): bool + public function commit(): void { try { - return $this->connection->exec('COMMIT'); + $this->connection->exec('COMMIT'); } catch (\Exception $e) { - return false; + throw Exception::new($e); } } - public function rollBack(): bool + public function rollBack(): void { try { - return $this->connection->exec('ROLLBACK'); + $this->connection->exec('ROLLBACK'); } catch (\Exception $e) { - return false; + throw Exception::new($e); } } diff --git a/doctrine/dbal/src/Driver/SQLite3/Driver.php b/doctrine/dbal/src/Driver/SQLite3/Driver.php index fecc4819d..e6996d366 100644 --- a/doctrine/dbal/src/Driver/SQLite3/Driver.php +++ b/doctrine/dbal/src/Driver/SQLite3/Driver.php @@ -1,9 +1,10 @@ enableExceptions(true); - UserDefinedFunctions::register([$connection, 'createFunction']); - return new Connection($connection); } } diff --git a/doctrine/dbal/src/Driver/SQLite3/Exception.php b/doctrine/dbal/src/Driver/SQLite3/Exception.php index 3ca1190bc..d4230042e 100644 --- a/doctrine/dbal/src/Driver/SQLite3/Exception.php +++ b/doctrine/dbal/src/Driver/SQLite3/Exception.php @@ -1,5 +1,7 @@ result = $result; - $this->changes = $changes; + $this->result = $result; } - /** @inheritDoc */ - public function fetchNumeric() + public function fetchNumeric(): array|false { if ($this->result === null) { return false; @@ -31,8 +30,7 @@ public function fetchNumeric() return $this->result->fetchArray(SQLITE3_NUM); } - /** @inheritDoc */ - public function fetchAssociative() + public function fetchAssociative(): array|false { if ($this->result === null) { return false; @@ -41,8 +39,7 @@ public function fetchAssociative() return $this->result->fetchArray(SQLITE3_ASSOC); } - /** @inheritDoc */ - public function fetchOne() + public function fetchOne(): mixed { return FetchUtils::fetchOne($this); } diff --git a/doctrine/dbal/src/Driver/SQLite3/Statement.php b/doctrine/dbal/src/Driver/SQLite3/Statement.php index a4166aa61..fe22e1df0 100644 --- a/doctrine/dbal/src/Driver/SQLite3/Statement.php +++ b/doctrine/dbal/src/Driver/SQLite3/Statement.php @@ -1,17 +1,15 @@ SQLITE3_NULL, - ParameterType::INTEGER => SQLITE3_INTEGER, - ParameterType::STRING => SQLITE3_TEXT, - ParameterType::ASCII => SQLITE3_TEXT, - ParameterType::BINARY => SQLITE3_BLOB, - ParameterType::LARGE_OBJECT => SQLITE3_BLOB, - ParameterType::BOOLEAN => SQLITE3_INTEGER, - ]; - - private SQLite3 $connection; - private SQLite3Stmt $statement; + private const TYPE_BLOB = SQLITE3_BLOB; + private const TYPE_INTEGER = SQLITE3_INTEGER; + private const TYPE_NULL = SQLITE3_NULL; + private const TYPE_TEXT = SQLITE3_TEXT; /** @internal The statement can be only instantiated by its driver connection. */ - public function __construct(SQLite3 $connection, SQLite3Stmt $statement) - { - $this->connection = $connection; - $this->statement = $statement; - } - - /** - * @throws UnknownParameterType - * - * {@inheritDoc} - * - * @psalm-assert ParameterType::* $type - */ - public function bindValue($param, $value, $type = ParameterType::STRING): bool - { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->statement->bindValue($param, $value, $this->convertParamType($type)); + public function __construct( + private readonly SQLite3 $connection, + private readonly SQLite3Stmt $statement, + ) { } - /** - * @throws UnknownParameterType - * - * {@inheritDoc} - * - * @psalm-assert ParameterType::* $type - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - return $this->statement->bindParam($param, $variable, $this->convertParamType($type)); + $this->statement->bindValue($param, $value, $this->convertParamType($type)); } - /** @inheritDoc */ - public function execute($params = null): Result + public function execute(): Result { - if ($params !== null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::execute() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - - foreach ($params as $param => $value) { - if (is_int($param)) { - $this->bindValue($param + 1, $value, ParameterType::STRING); - } else { - $this->bindValue($param, $value, ParameterType::STRING); - } - } - } - try { $result = $this->statement->execute(); } catch (\Exception $e) { @@ -120,17 +48,14 @@ public function execute($params = null): Result return new Result($result, $this->connection->changes()); } - /** - * @psalm-return value-of - * - * @psalm-assert ParameterType::* $type - */ - private function convertParamType(int $type): int + /** @psalm-return self::TYPE_* */ + private function convertParamType(ParameterType $type): int { - if (! isset(self::PARAM_TYPE_MAP[$type])) { - throw UnknownParameterType::new($type); - } - - return self::PARAM_TYPE_MAP[$type]; + return match ($type) { + ParameterType::NULL => self::TYPE_NULL, + ParameterType::INTEGER, ParameterType::BOOLEAN => self::TYPE_INTEGER, + ParameterType::STRING, ParameterType::ASCII => self::TYPE_TEXT, + ParameterType::BINARY, ParameterType::LARGE_OBJECT => self::TYPE_BLOB, + }; } } diff --git a/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php b/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php deleted file mode 100644 index 5687ab0bb..000000000 --- a/doctrine/dbal/src/Driver/ServerInfoAwareConnection.php +++ /dev/null @@ -1,21 +0,0 @@ -execute() is called. - * - * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), - * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. - * - * Most parameters are input parameters, that is, parameters that are - * used in a read-only fashion to build up the query. Some drivers support the invocation - * of stored procedures that return data as output parameters, and some also as input/output - * parameters that both send in data and are updated to receive it. - * - * @deprecated Use {@see bindValue()} instead. - * - * @param string|int $param Parameter identifier. For a prepared statement using named placeholders, - * this will be a parameter name of the form :name. For a prepared statement using - * 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 $type Explicit data type for the parameter using the {@see ParameterType} + * @param int|string $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position + * of the parameter. + * @param mixed $value The value to bind to the parameter. + * @param ParameterType $type Explicit data type for the parameter using the {@see ParameterType} * 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. - * - * @return bool TRUE on success or FALSE on failure. * * @throws Exception */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null); + public function bindValue(int|string $param, mixed $value, ParameterType $type): void; /** * Executes a prepared statement * - * If the prepared statement included parameter markers, you must either: - * call {@see bindParam()} to bind PHP variables to the parameter markers: - * bound variables pass their value as input and receive the output value, - * if any, of their associated parameter markers or pass an array of input-only - * parameter values. - * - * @param mixed[]|null $params A numeric array of values with as many elements as there are - * bound parameters in the SQL statement being executed. - * * @throws Exception */ - public function execute($params = null): Result; + public function execute(): Result; } diff --git a/doctrine/dbal/src/DriverManager.php b/doctrine/dbal/src/DriverManager.php index 056f42084..8b41cbbe1 100644 --- a/doctrine/dbal/src/DriverManager.php +++ b/doctrine/dbal/src/DriverManager.php @@ -1,8 +1,9 @@ , * driver?: key-of, * driverClass?: class-string, * driverOptions?: array, * host?: string, + * memory?: bool, * password?: string, * path?: string, * persistent?: bool, - * platform?: Platforms\AbstractPlatform, * port?: int, * serverVersion?: string, - * url?: string, + * sessionMode?: int, * user?: string, * unix_socket?: string, + * wrapperClass?: class-string, * } * @psalm-type Params = array{ * application_name?: string, * charset?: string, * dbname?: string, * defaultTableOptions?: array, - * default_dbname?: string, * driver?: key-of, * driverClass?: class-string, * driverOptions?: array, * host?: string, - * keepSlave?: bool, * keepReplica?: bool, - * master?: OverrideParams, * memory?: bool, * password?: string, * path?: string, * persistent?: bool, - * platform?: Platforms\AbstractPlatform, * port?: int, * primary?: OverrideParams, * replica?: array, * serverVersion?: string, - * sharding?: array, - * slaves?: array, - * url?: string, + * sessionMode?: int, * user?: string, * wrapperClass?: class-string, * unix_socket?: string, @@ -79,37 +75,17 @@ final class DriverManager * To add your own driver use the 'driverClass' parameter to {@see DriverManager::getConnection()}. */ private const DRIVER_MAP = [ - 'pdo_mysql' => PDO\MySQL\Driver::class, - 'pdo_sqlite' => PDO\SQLite\Driver::class, - 'pdo_pgsql' => PDO\PgSQL\Driver::class, - 'pdo_oci' => PDO\OCI\Driver::class, - 'oci8' => OCI8\Driver::class, - 'ibm_db2' => IBMDB2\Driver::class, - 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, - 'mysqli' => Mysqli\Driver::class, - 'pgsql' => PgSQL\Driver::class, - 'sqlsrv' => SQLSrv\Driver::class, - 'sqlite3' => SQLite3\Driver::class, - ]; - - /** - * List of URL schemes from a database URL and their mappings to driver. - * - * @deprecated Use actual driver names instead. - * - * @var array - * @psalm-var array> - */ - private static array $driverSchemeAliases = [ - 'db2' => 'ibm_db2', - 'mssql' => 'pdo_sqlsrv', - 'mysql' => 'pdo_mysql', - 'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason - 'postgres' => 'pdo_pgsql', - 'postgresql' => 'pdo_pgsql', - 'pgsql' => 'pdo_pgsql', - 'sqlite' => 'pdo_sqlite', - 'sqlite3' => 'pdo_sqlite', + 'pdo_mysql' => PDO\MySQL\Driver::class, + 'pdo_sqlite' => PDO\SQLite\Driver::class, + 'pdo_pgsql' => PDO\PgSQL\Driver::class, + 'pdo_oci' => PDO\OCI\Driver::class, + 'oci8' => OCI8\Driver::class, + 'ibm_db2' => IBMDB2\Driver::class, + 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, + 'mysqli' => Mysqli\Driver::class, + 'pgsql' => PgSQL\Driver::class, + 'sqlsrv' => SQLSrv\Driver::class, + 'sqlite3' => SQLite3\Driver::class, ]; /** @@ -151,50 +127,32 @@ private function __construct() * driverClass: * The driver class to use. * - * @param Configuration|null $config The configuration to use. - * @param EventManager|null $eventManager The event manager to use. + * @param Configuration|null $config The configuration to use. * @psalm-param Params $params * * @psalm-return ($params is array{wrapperClass: class-string} ? T : Connection) * - * @throws Exception - * * @template T of Connection */ public static function getConnection( #[SensitiveParameter] array $params, ?Configuration $config = null, - ?EventManager $eventManager = null ): Connection { - // create default config and event manager, if not set - $config ??= new Configuration(); - $eventManager ??= new EventManager(); - $params = self::parseDatabaseUrl($params); - - // URL support for PrimaryReplicaConnection - if (isset($params['primary'])) { - $params['primary'] = self::parseDatabaseUrl($params['primary']); - } - - if (isset($params['replica'])) { - foreach ($params['replica'] as $key => $replicaParams) { - $params['replica'][$key] = self::parseDatabaseUrl($replicaParams); - } - } - - $driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null); + $config ??= new Configuration(); + $driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null); foreach ($config->getMiddlewares() as $middleware) { $driver = $middleware->wrap($driver); } + /** @var class-string $wrapperClass */ $wrapperClass = $params['wrapperClass'] ?? Connection::class; if (! is_a($wrapperClass, Connection::class, true)) { - throw Exception::invalidWrapperClass($wrapperClass); + throw InvalidWrapperClass::new($wrapperClass); } - return new $wrapperClass($params, $driver, $config, $eventManager); + return new $wrapperClass($params, $driver, $config); } /** @@ -209,80 +167,25 @@ public static function getAvailableDrivers(): array } /** - * @throws Exception - * - * @psalm-assert key-of|null $driver - * @psalm-assert class-string|null $driverClass + * @param class-string|null $driverClass + * @param key-of|null $driver */ private static function createDriver(?string $driver, ?string $driverClass): Driver { if ($driverClass === null) { if ($driver === null) { - throw Exception::driverRequired(); + throw DriverRequired::new(); } if (! isset(self::DRIVER_MAP[$driver])) { - throw Exception::unknownDriver($driver, array_keys(self::DRIVER_MAP)); + throw UnknownDriver::new($driver, array_keys(self::DRIVER_MAP)); } $driverClass = self::DRIVER_MAP[$driver]; } elseif (! is_a($driverClass, Driver::class, true)) { - throw Exception::invalidDriverClass($driverClass); + throw InvalidDriverClass::new($driverClass); } return new $driverClass(); } - - /** - * Extracts parts from a database URL, if present, and returns an - * updated list of parameters. - * - * @param mixed[] $params The list of parameters. - * @psalm-param Params $params - * - * @return mixed[] A modified list of parameters with info from a database - * URL extracted into indidivual parameter parts. - * @psalm-return Params - * - * @throws Exception - */ - private static function parseDatabaseUrl( - #[SensitiveParameter] - array $params - ): array { - if (! isset($params['url'])) { - return $params; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5843', - 'The "url" connection parameter is deprecated. Please use %s to parse a database url before calling %s.', - DsnParser::class, - self::class, - ); - - $parser = new DsnParser(self::$driverSchemeAliases); - try { - $parsedParams = $parser->parse($params['url']); - } catch (MalformedDsnException $e) { - throw new Exception('Malformed parameter "url".', 0, $e); - } - - if (isset($parsedParams['driver'])) { - // The requested driver from the URL scheme takes precedence - // over the default custom driver from the connection parameters (if any). - unset($params['driverClass']); - } - - $params = array_merge($params, $parsedParams); - - // If a schemeless connection URL is given, we require a default driver or default custom driver - // as connection parameter. - if (! isset($params['driverClass']) && ! isset($params['driver'])) { - throw Exception::driverRequired($params['url']); - } - - return $params; - } } diff --git a/doctrine/dbal/src/Event/ConnectionEventArgs.php b/doctrine/dbal/src/Event/ConnectionEventArgs.php deleted file mode 100644 index 9a69c2541..000000000 --- a/doctrine/dbal/src/Event/ConnectionEventArgs.php +++ /dev/null @@ -1,27 +0,0 @@ -connection = $connection; - } - - /** @return Connection */ - public function getConnection() - { - return $this->connection; - } -} diff --git a/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php b/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php deleted file mode 100644 index 9598f43cc..000000000 --- a/doctrine/dbal/src/Event/Listeners/OracleSessionInit.php +++ /dev/null @@ -1,77 +0,0 @@ - 'HH24:MI:SS', - 'NLS_DATE_FORMAT' => 'YYYY-MM-DD HH24:MI:SS', - 'NLS_TIMESTAMP_FORMAT' => 'YYYY-MM-DD HH24:MI:SS', - 'NLS_TIMESTAMP_TZ_FORMAT' => 'YYYY-MM-DD HH24:MI:SS TZH:TZM', - 'NLS_NUMERIC_CHARACTERS' => '.,', - ]; - - /** @param string[] $oracleSessionVars */ - public function __construct(array $oracleSessionVars = []) - { - $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); - } - - /** - * @return void - * - * @throws Exception - */ - public function postConnect(ConnectionEventArgs $args) - { - if (count($this->_defaultSessionVars) === 0) { - return; - } - - $vars = []; - foreach (array_change_key_case($this->_defaultSessionVars, CASE_UPPER) as $option => $value) { - if ($option === 'CURRENT_SCHEMA') { - $vars[] = $option . ' = ' . $value; - } else { - $vars[] = $option . " = '" . $value . "'"; - } - } - - $sql = 'ALTER SESSION SET ' . implode(' ', $vars); - $args->getConnection()->executeStatement($sql); - } - - /** - * {@inheritDoc} - */ - public function getSubscribedEvents() - { - return [Events::postConnect]; - } -} diff --git a/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php deleted file mode 100644 index 4ce32d628..000000000 --- a/doctrine/dbal/src/Event/Listeners/SQLSessionInit.php +++ /dev/null @@ -1,43 +0,0 @@ -sql = $sql; - } - - /** - * @return void - * - * @throws Exception - */ - public function postConnect(ConnectionEventArgs $args) - { - $args->getConnection()->executeStatement($this->sql); - } - - /** - * {@inheritDoc} - */ - public function getSubscribedEvents() - { - return [Events::postConnect]; - } -} diff --git a/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php b/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php deleted file mode 100644 index 950f05f48..000000000 --- a/doctrine/dbal/src/Event/Listeners/SQLiteSessionInit.php +++ /dev/null @@ -1,30 +0,0 @@ -getConnection()->executeStatement('PRAGMA foreign_keys=ON'); - } - - /** - * {@inheritDoc} - */ - public function getSubscribedEvents() - { - return [Events::postConnect]; - } -} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php deleted file mode 100644 index 9f3ff6ea7..000000000 --- a/doctrine/dbal/src/Event/SchemaAlterTableAddColumnEventArgs.php +++ /dev/null @@ -1,81 +0,0 @@ -column = $column; - $this->tableDiff = $tableDiff; - $this->platform = $platform; - } - - /** @return Column */ - public function getColumn() - { - return $this->column; - } - - /** @return TableDiff */ - public function getTableDiff() - { - return $this->tableDiff; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaAlterTableAddColumnEventArgs - */ - public function addSql($sql) - { - if (is_array($sql)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'Passing multiple SQL statements as an array to SchemaAlterTableAddColumnEventaArrgs::addSql() ' . - 'is deprecated. Pass each statement as an individual argument instead.', - ); - } - - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php deleted file mode 100644 index 9ba37aade..000000000 --- a/doctrine/dbal/src/Event/SchemaAlterTableChangeColumnEventArgs.php +++ /dev/null @@ -1,71 +0,0 @@ -columnDiff = $columnDiff; - $this->tableDiff = $tableDiff; - $this->platform = $platform; - } - - /** @return ColumnDiff */ - public function getColumnDiff() - { - return $this->columnDiff; - } - - /** @return TableDiff */ - public function getTableDiff() - { - return $this->tableDiff; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaAlterTableChangeColumnEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php deleted file mode 100644 index 07c065a9a..000000000 --- a/doctrine/dbal/src/Event/SchemaAlterTableEventArgs.php +++ /dev/null @@ -1,62 +0,0 @@ -tableDiff = $tableDiff; - $this->platform = $platform; - } - - /** @return TableDiff */ - public function getTableDiff() - { - return $this->tableDiff; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaAlterTableEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php deleted file mode 100644 index 4122b418c..000000000 --- a/doctrine/dbal/src/Event/SchemaAlterTableRemoveColumnEventArgs.php +++ /dev/null @@ -1,71 +0,0 @@ -column = $column; - $this->tableDiff = $tableDiff; - $this->platform = $platform; - } - - /** @return Column */ - public function getColumn() - { - return $this->column; - } - - /** @return TableDiff */ - public function getTableDiff() - { - return $this->tableDiff; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaAlterTableRemoveColumnEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php deleted file mode 100644 index 21d3c1645..000000000 --- a/doctrine/dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.php +++ /dev/null @@ -1,82 +0,0 @@ -oldColumnName = $oldColumnName; - $this->column = $column; - $this->tableDiff = $tableDiff; - $this->platform = $platform; - } - - /** @return string */ - public function getOldColumnName() - { - return $this->oldColumnName; - } - - /** @return Column */ - public function getColumn() - { - return $this->column; - } - - /** @return TableDiff */ - public function getTableDiff() - { - return $this->tableDiff; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaAlterTableRenameColumnEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php deleted file mode 100644 index 04fcbde9c..000000000 --- a/doctrine/dbal/src/Event/SchemaColumnDefinitionEventArgs.php +++ /dev/null @@ -1,87 +0,0 @@ -tableColumn = $tableColumn; - $this->table = $table; - $this->database = $database; - $this->connection = $connection; - } - - /** - * Allows to clear the column which means the column will be excluded from - * tables column list. - * - * @return SchemaColumnDefinitionEventArgs - */ - public function setColumn(?Column $column = null) - { - $this->column = $column; - - return $this; - } - - /** @return Column|null */ - public function getColumn() - { - return $this->column; - } - - /** @return mixed[] */ - public function getTableColumn() - { - return $this->tableColumn; - } - - /** @return string */ - public function getTable() - { - return $this->table; - } - - /** @return string */ - public function getDatabase() - { - return $this->database; - } - - /** @return Connection */ - public function getConnection() - { - return $this->connection; - } -} diff --git a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php deleted file mode 100644 index 54f134d14..000000000 --- a/doctrine/dbal/src/Event/SchemaCreateTableColumnEventArgs.php +++ /dev/null @@ -1,71 +0,0 @@ -column = $column; - $this->table = $table; - $this->platform = $platform; - } - - /** @return Column */ - public function getColumn() - { - return $this->column; - } - - /** @return Table */ - public function getTable() - { - return $this->table; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaCreateTableColumnEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php b/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php deleted file mode 100644 index a7d548deb..000000000 --- a/doctrine/dbal/src/Event/SchemaCreateTableEventArgs.php +++ /dev/null @@ -1,87 +0,0 @@ -table = $table; - $this->columns = $columns; - $this->options = $options; - $this->platform = $platform; - } - - /** @return Table */ - public function getTable() - { - return $this->table; - } - - /** @return mixed[][] */ - public function getColumns() - { - return $this->columns; - } - - /** @return mixed[] */ - public function getOptions() - { - return $this->options; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. - * - * @param string|string[] $sql - * - * @return SchemaCreateTableEventArgs - */ - public function addSql($sql) - { - $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); - - return $this; - } - - /** @return string[] */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php b/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php deleted file mode 100644 index f45e3a15a..000000000 --- a/doctrine/dbal/src/Event/SchemaDropTableEventArgs.php +++ /dev/null @@ -1,64 +0,0 @@ -table = $table; - $this->platform = $platform; - } - - /** @return string|Table */ - public function getTable() - { - return $this->table; - } - - /** @return AbstractPlatform */ - public function getPlatform() - { - return $this->platform; - } - - /** - * @param string $sql - * - * @return SchemaDropTableEventArgs - */ - public function setSql($sql) - { - $this->sql = $sql; - - return $this; - } - - /** @return string|null */ - public function getSql() - { - return $this->sql; - } -} diff --git a/doctrine/dbal/src/Event/SchemaEventArgs.php b/doctrine/dbal/src/Event/SchemaEventArgs.php deleted file mode 100644 index 77d1d3908..000000000 --- a/doctrine/dbal/src/Event/SchemaEventArgs.php +++ /dev/null @@ -1,29 +0,0 @@ -preventDefault = true; - - return $this; - } - - /** @return bool */ - public function isDefaultPrevented() - { - return $this->preventDefault; - } -} diff --git a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php b/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php deleted file mode 100644 index dbee55a0e..000000000 --- a/doctrine/dbal/src/Event/SchemaIndexDefinitionEventArgs.php +++ /dev/null @@ -1,75 +0,0 @@ -tableIndex = $tableIndex; - $this->table = $table; - $this->connection = $connection; - } - - /** - * Allows to clear the index which means the index will be excluded from tables index list. - * - * @return SchemaIndexDefinitionEventArgs - */ - public function setIndex(?Index $index = null) - { - $this->index = $index; - - return $this; - } - - /** @return Index|null */ - public function getIndex() - { - return $this->index; - } - - /** @return mixed[] */ - public function getTableIndex() - { - return $this->tableIndex; - } - - /** @return string */ - public function getTable() - { - return $this->table; - } - - /** @return Connection */ - public function getConnection() - { - return $this->connection; - } -} diff --git a/doctrine/dbal/src/Event/TransactionBeginEventArgs.php b/doctrine/dbal/src/Event/TransactionBeginEventArgs.php deleted file mode 100644 index be4ccdf15..000000000 --- a/doctrine/dbal/src/Event/TransactionBeginEventArgs.php +++ /dev/null @@ -1,10 +0,0 @@ -connection = $connection; - } - - public function getConnection(): Connection - { - return $this->connection; - } -} diff --git a/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php b/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php deleted file mode 100644 index 9e6e650d8..000000000 --- a/doctrine/dbal/src/Event/TransactionRollBackEventArgs.php +++ /dev/null @@ -1,10 +0,0 @@ -getMessage(); } else { @@ -35,17 +34,12 @@ public function __construct(TheDriverException $driverException, ?Query $query) } parent::__construct($message, $driverException->getCode(), $driverException); - - $this->query = $query; } - /** - * {@inheritDoc} - */ - public function getSQLState() + public function getSQLState(): ?string { $previous = $this->getPrevious(); - assert($previous instanceof TheDriverException); + assert($previous instanceof Driver\Exception); return $previous->getSQLState(); } diff --git a/doctrine/dbal/src/Exception/DriverRequired.php b/doctrine/dbal/src/Exception/DriverRequired.php new file mode 100644 index 000000000..e37ac6860 --- /dev/null +++ b/doctrine/dbal/src/Exception/DriverRequired.php @@ -0,0 +1,30 @@ +|array */ - private array $originalParameters; - - /** @var array|array */ - private array $originalTypes; - private int $originalParameterIndex = 0; /** @var list */ @@ -29,28 +26,28 @@ final class ExpandArrayParameters implements Visitor /** @var list */ private array $convertedParameters = []; - /** @var array */ + /** @var array,string|ParameterType|Type> */ private array $convertedTypes = []; /** - * @param array|array $parameters - * @param array|array $types + * @param array|array $parameters + * @psalm-param WrapperParameterTypeArray $types */ - public function __construct(array $parameters, array $types) - { - $this->originalParameters = $parameters; - $this->originalTypes = $types; + public function __construct( + private readonly array $parameters, + private readonly array $types, + ) { } public function acceptPositionalParameter(string $sql): void { $index = $this->originalParameterIndex; - if (! array_key_exists($index, $this->originalParameters)) { + if (! array_key_exists($index, $this->parameters)) { throw MissingPositionalParameter::new($index); } - $this->acceptParameter($index, $this->originalParameters[$index]); + $this->acceptParameter($index, $this->parameters[$index]); $this->originalParameterIndex++; } @@ -59,11 +56,11 @@ public function acceptNamedParameter(string $sql): void { $name = substr($sql, 1); - if (! array_key_exists($name, $this->originalParameters)) { + if (! array_key_exists($name, $this->parameters)) { throw MissingNamedParameter::new($name); } - $this->acceptParameter($name, $this->originalParameters[$name]); + $this->acceptParameter($name, $this->parameters[$name]); } public function acceptOther(string $sql): void @@ -82,27 +79,18 @@ public function getParameters(): array return $this->convertedParameters; } - /** - * @param int|string $key - * @param mixed $value - */ - private function acceptParameter($key, $value): void + private function acceptParameter(int|string $key, mixed $value): void { - if (! isset($this->originalTypes[$key])) { + if (! isset($this->types[$key])) { $this->convertedSQL[] = '?'; $this->convertedParameters[] = $value; return; } - $type = $this->originalTypes[$key]; + $type = $this->types[$key]; - if ( - $type !== ArrayParameterType::INTEGER - && $type !== ArrayParameterType::STRING - && $type !== ArrayParameterType::ASCII - && $type !== ArrayParameterType::BINARY - ) { + if (! $type instanceof ArrayParameterType) { $this->appendTypedParameter([$value], $type); return; @@ -117,17 +105,14 @@ private function acceptParameter($key, $value): void $this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type)); } - /** @return array */ + /** @return array,string|ParameterType|Type> */ public function getTypes(): array { return $this->convertedTypes; } - /** - * @param list $values - * @param Type|int|string|null $type - */ - private function appendTypedParameter(array $values, $type): void + /** @param list $values */ + private function appendTypedParameter(array $values, string|ParameterType|Type $type): void { $this->convertedSQL[] = implode(', ', array_fill(0, count($values), '?')); diff --git a/doctrine/dbal/src/FetchMode.php b/doctrine/dbal/src/FetchMode.php deleted file mode 100644 index d80719f25..000000000 --- a/doctrine/dbal/src/FetchMode.php +++ /dev/null @@ -1,20 +0,0 @@ -getDriver() instanceof Driver\PDO\SQLite\Driver) { - throw new Exception('Cannot use TableGenerator with SQLite.'); - } - - $this->conn = DriverManager::getConnection( - $conn->getParams(), - $conn->getConfiguration(), - $conn->getEventManager(), - ); - - $this->generatorTableName = $generatorTableName; - } - - /** - * Generates the next unused value for the given sequence name. - * - * @param string $sequence - * - * @return int - * - * @throws Exception - */ - public function nextValue($sequence) - { - if (isset($this->sequences[$sequence])) { - $value = $this->sequences[$sequence]['value']; - $this->sequences[$sequence]['value']++; - if ($this->sequences[$sequence]['value'] >= $this->sequences[$sequence]['max']) { - unset($this->sequences[$sequence]); - } - - return $value; - } - - $this->conn->beginTransaction(); - - try { - $row = $this->conn->createQueryBuilder() - ->select('sequence_value', 'sequence_increment_by') - ->from($this->generatorTableName) - ->where('sequence_name = ?') - ->forUpdate() - ->setParameter(1, $sequence) - ->fetchAssociative(); - - if ($row !== false) { - $row = array_change_key_case($row, CASE_LOWER); - - $value = $row['sequence_value']; - $value++; - - assert(is_int($value)); - - if ($row['sequence_increment_by'] > 1) { - $this->sequences[$sequence] = [ - 'value' => $value, - 'max' => $row['sequence_value'] + $row['sequence_increment_by'], - ]; - } - - $sql = 'UPDATE ' . $this->generatorTableName . ' ' . - 'SET sequence_value = sequence_value + sequence_increment_by ' . - 'WHERE sequence_name = ? AND sequence_value = ?'; - $rows = $this->conn->executeStatement($sql, [$sequence, $row['sequence_value']]); - - if ($rows !== 1) { - throw new Exception('Race-condition detected while updating sequence. Aborting generation'); - } - } else { - $this->conn->insert( - $this->generatorTableName, - ['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1], - ); - $value = 1; - } - - $this->conn->commit(); - } catch (Throwable $e) { - $this->conn->rollBack(); - - throw new Exception( - 'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(), - 0, - $e, - ); - } - - return $value; - } -} diff --git a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php b/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php deleted file mode 100644 index 75c9fe9cf..000000000 --- a/doctrine/dbal/src/Id/TableGeneratorSchemaVisitor.php +++ /dev/null @@ -1,77 +0,0 @@ -generatorTableName = $generatorTableName; - } - - /** - * {@inheritDoc} - */ - public function acceptSchema(Schema $schema) - { - $table = $schema->createTable($this->generatorTableName); - $table->addColumn('sequence_name', 'string'); - $table->addColumn('sequence_value', 'integer', ['default' => 1]); - $table->addColumn('sequence_increment_by', 'integer', ['default' => 1]); - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - } - - /** - * {@inheritDoc} - */ - public function acceptColumn(Table $table, Column $column) - { - } - - /** - * {@inheritDoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - } - - /** - * {@inheritDoc} - */ - public function acceptIndex(Table $table, Index $index) - { - } - - /** - * {@inheritDoc} - */ - public function acceptSequence(Sequence $sequence) - { - } -} diff --git a/doctrine/dbal/src/LockMode.php b/doctrine/dbal/src/LockMode.php index 1a7ed2397..035353e02 100644 --- a/doctrine/dbal/src/LockMode.php +++ b/doctrine/dbal/src/LockMode.php @@ -1,23 +1,16 @@ logger = $logger; } public function __destruct() @@ -43,40 +39,31 @@ public function query(string $sql): Result return parent::query($sql); } - public function exec(string $sql): int + public function exec(string $sql): int|string { $this->logger->debug('Executing statement: {sql}', ['sql' => $sql]); return parent::exec($sql); } - /** - * {@inheritDoc} - */ - public function beginTransaction() + public function beginTransaction(): void { $this->logger->debug('Beginning transaction'); - return parent::beginTransaction(); + parent::beginTransaction(); } - /** - * {@inheritDoc} - */ - public function commit() + public function commit(): void { $this->logger->debug('Committing transaction'); - return parent::commit(); + parent::commit(); } - /** - * {@inheritDoc} - */ - public function rollBack() + public function rollBack(): void { $this->logger->debug('Rolling back transaction'); - return parent::rollBack(); + parent::rollBack(); } } diff --git a/doctrine/dbal/src/Logging/DebugStack.php b/doctrine/dbal/src/Logging/DebugStack.php deleted file mode 100644 index 1a970d060..000000000 --- a/doctrine/dbal/src/Logging/DebugStack.php +++ /dev/null @@ -1,75 +0,0 @@ -> - */ - public $queries = []; - - /** - * If Debug Stack is enabled (log queries) or not. - * - * @var bool - */ - public $enabled = true; - - /** @var float|null */ - public $start = null; - - /** @var int */ - public $currentQuery = 0; - - public function __construct() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4967', - 'DebugStack is deprecated.', - ); - } - - /** - * {@inheritDoc} - */ - public function startQuery($sql, ?array $params = null, ?array $types = null) - { - if (! $this->enabled) { - return; - } - - $this->start = microtime(true); - - $this->queries[++$this->currentQuery] = [ - 'sql' => $sql, - 'params' => $params, - 'types' => $types, - 'executionMS' => 0, - ]; - } - - /** - * {@inheritDoc} - */ - public function stopQuery() - { - if (! $this->enabled) { - return; - } - - $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; - } -} diff --git a/doctrine/dbal/src/Logging/Driver.php b/doctrine/dbal/src/Logging/Driver.php index 32a5cd2c2..35acd393b 100644 --- a/doctrine/dbal/src/Logging/Driver.php +++ b/doctrine/dbal/src/Logging/Driver.php @@ -11,14 +11,10 @@ final class Driver extends AbstractDriverMiddleware { - private LoggerInterface $logger; - /** @internal This driver can be only instantiated by its middleware. */ - public function __construct(DriverInterface $driver, LoggerInterface $logger) + public function __construct(DriverInterface $driver, private readonly LoggerInterface $logger) { parent::__construct($driver); - - $this->logger = $logger; } /** @@ -26,8 +22,8 @@ public function __construct(DriverInterface $driver, LoggerInterface $logger) */ public function connect( #[SensitiveParameter] - array $params - ) { + array $params, + ): Connection { $this->logger->info('Connecting with parameters {params}', ['params' => $this->maskPassword($params)]); return new Connection( @@ -43,16 +39,12 @@ public function connect( */ private function maskPassword( #[SensitiveParameter] - array $params + array $params, ): array { if (isset($params['password'])) { $params['password'] = ''; } - if (isset($params['url'])) { - $params['url'] = ''; - } - return $params; } } diff --git a/doctrine/dbal/src/Logging/LoggerChain.php b/doctrine/dbal/src/Logging/LoggerChain.php deleted file mode 100644 index 7a4eaa49a..000000000 --- a/doctrine/dbal/src/Logging/LoggerChain.php +++ /dev/null @@ -1,48 +0,0 @@ - */ - private iterable $loggers; - - /** @param iterable $loggers */ - public function __construct(iterable $loggers = []) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4967', - 'LoggerChain is deprecated', - ); - - $this->loggers = $loggers; - } - - /** - * {@inheritDoc} - */ - public function startQuery($sql, ?array $params = null, ?array $types = null) - { - foreach ($this->loggers as $logger) { - $logger->startQuery($sql, $params, $types); - } - } - - /** - * {@inheritDoc} - */ - public function stopQuery() - { - foreach ($this->loggers as $logger) { - $logger->stopQuery(); - } - } -} diff --git a/doctrine/dbal/src/Logging/Middleware.php b/doctrine/dbal/src/Logging/Middleware.php index da0c1b90f..989e0ca26 100644 --- a/doctrine/dbal/src/Logging/Middleware.php +++ b/doctrine/dbal/src/Logging/Middleware.php @@ -10,11 +10,8 @@ final class Middleware implements MiddlewareInterface { - private LoggerInterface $logger; - - public function __construct(LoggerInterface $logger) + public function __construct(private readonly LoggerInterface $logger) { - $this->logger = $logger; } public function wrap(DriverInterface $driver): DriverInterface diff --git a/doctrine/dbal/src/Logging/SQLLogger.php b/doctrine/dbal/src/Logging/SQLLogger.php deleted file mode 100644 index dab4a3a7d..000000000 --- a/doctrine/dbal/src/Logging/SQLLogger.php +++ /dev/null @@ -1,32 +0,0 @@ -|array|null $params Statement parameters - * @param array|array|null $types Parameter types - * - * @return void - */ - public function startQuery($sql, ?array $params = null, ?array $types = null); - - /** - * Marks the last started query as stopped. This can be used for timing of queries. - * - * @return void - */ - public function stopQuery(); -} diff --git a/doctrine/dbal/src/Logging/Statement.php b/doctrine/dbal/src/Logging/Statement.php index 039b93b6a..ed1ca7f2d 100644 --- a/doctrine/dbal/src/Logging/Statement.php +++ b/doctrine/dbal/src/Logging/Statement.php @@ -8,93 +8,41 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; -use Doctrine\Deprecations\Deprecation; use Psr\Log\LoggerInterface; -use function array_slice; -use function func_get_args; -use function func_num_args; - final class Statement extends AbstractStatementMiddleware { - private LoggerInterface $logger; - private string $sql; - /** @var array|array */ private array $params = []; - /** @var array|array */ + /** @var array|array */ private array $types = []; /** @internal This statement can be only instantiated by its connection. */ - public function __construct(StatementInterface $statement, LoggerInterface $logger, string $sql) - { + public function __construct( + StatementInterface $statement, + private readonly LoggerInterface $logger, + private readonly string $sql, + ) { parent::__construct($statement); - - $this->logger = $logger; - $this->sql = $sql; - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see bindValue()} instead. - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindParam() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - - $this->params[$param] = &$variable; - $this->types[$param] = $type; - - return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); } - /** - * {@inheritDoc} - */ - public function bindValue($param, $value, $type = ParameterType::STRING) + public function bindValue(int|string $param, mixed $value, ParameterType $type): void { - if (func_num_args() < 3) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5558', - 'Not passing $type to Statement::bindValue() is deprecated.' - . ' Pass the type corresponding to the parameter being bound.', - ); - } - $this->params[$param] = $value; $this->types[$param] = $type; - return parent::bindValue($param, $value, $type); + parent::bindValue($param, $value, $type); } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): ResultInterface { $this->logger->debug('Executing statement: {sql} (parameters: {params}, types: {types})', [ 'sql' => $this->sql, - 'params' => $params ?? $this->params, + 'params' => $this->params, 'types' => $this->types, ]); - return parent::execute($params); + return parent::execute(); } } diff --git a/doctrine/dbal/src/ParameterType.php b/doctrine/dbal/src/ParameterType.php index 77917e870..19f577ec3 100644 --- a/doctrine/dbal/src/ParameterType.php +++ b/doctrine/dbal/src/ParameterType.php @@ -1,57 +1,46 @@ value . ')'; } - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) + public function getDateDiffExpression(string $date1, string $date2): string { return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; } @@ -138,125 +104,29 @@ public function getCurrentDatabaseExpression(): string return 'DATABASE()'; } - /** - * {@inheritDoc} - */ - public function getLengthExpression($column) + public function getLengthExpression(string $string): string { - return 'CHAR_LENGTH(' . $column . ')'; + return 'CHAR_LENGTH(' . $string . ')'; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListDatabasesSQL() + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListDatabasesSQL(): string { return 'SHOW DATABASES'; } - /** - * @deprecated - * - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - return 'SHOW INDEX FROM ' . $table; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - * - * Two approaches to listing the table indexes. The information_schema is - * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". - */ - public function getListTableIndexesSQL($table, $database = null) - { - if ($database !== null) { - return 'SELECT NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name,' . - ' SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type' . - ' FROM information_schema.STATISTICS WHERE TABLE_NAME = ' . $this->quoteStringLiteral($table) . - ' AND TABLE_SCHEMA = ' . $this->quoteStringLiteral($database) . - ' ORDER BY SEQ_IN_INDEX ASC'; - } - - return 'SHOW INDEX FROM ' . $table; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string { return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $this->quoteStringLiteral($database); } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - // The schema name is passed multiple times as a literal in the WHERE clause instead of using a JOIN condition - // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions - // caused by https://bugs.mysql.com/bug.php?id=81347 - return 'SELECT k.CONSTRAINT_NAME, k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, ' . - 'k.REFERENCED_COLUMN_NAME /*!50116 , c.UPDATE_RULE, c.DELETE_RULE */ ' . - 'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k /*!50116 ' . - 'INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS c ON ' . - 'c.CONSTRAINT_NAME = k.CONSTRAINT_NAME AND ' . - 'c.TABLE_NAME = k.TABLE_NAME */ ' . - 'WHERE k.TABLE_NAME = ' . $this->quoteStringLiteral($table) . ' ' . - 'AND k.TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' /*!50116 ' . - 'AND c.CONSTRAINT_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' */' . - 'ORDER BY k.ORDINAL_POSITION'; - } - /** * {@inheritDoc} */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + public function getJsonTypeDeclarationSQL(array $column): string { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default string column length on MySQL is deprecated' - . ', specify the length explicitly.', - ); - } - - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); - } - - /** - * {@inheritDoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) - { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length on MySQL is deprecated' - . ', specify the length explicitly.', - ); - } - - return $fixed - ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' - : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; + return 'JSON'; } /** @@ -268,7 +138,7 @@ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $length * * {@inheritDoc} */ - public function getClobTypeDeclarationSQL(array $column) + public function getClobTypeDeclarationSQL(array $column): string { if (! empty($column['length']) && is_numeric($column['length'])) { $length = $column['length']; @@ -292,7 +162,7 @@ public function getClobTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getDateTimeTypeDeclarationSQL(array $column): string { if (isset($column['version']) && $column['version'] === true) { return 'TIMESTAMP'; @@ -304,7 +174,7 @@ public function getDateTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTypeDeclarationSQL(array $column) + public function getDateTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -312,7 +182,7 @@ public function getDateTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getTimeTypeDeclarationSQL(array $column) + public function getTimeTypeDeclarationSQL(array $column): string { return 'TIME'; } @@ -320,188 +190,60 @@ public function getTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBooleanTypeDeclarationSQL(array $column) + public function getBooleanTypeDeclarationSQL(array $column): string { return 'TINYINT(1)'; } - /** - * {@inheritDoc} - * - * @deprecated - * - * MySQL prefers "autoincrement" identity columns since sequences can only - * be emulated with a table. - */ - public function prefersIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/1519', - 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.', - ); - - return true; - } - /** * {@inheritDoc} * * MySQL supports this through AUTO_INCREMENT columns. */ - public function supportsIdentityColumns() + public function supportsIdentityColumns(): bool { return true; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsInlineColumnComments() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsInlineColumnComments(): bool { return true; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsColumnCollation() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsColumnCollation(): bool { return true; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . - 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . - 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . - 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . - ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table) . - ' ORDER BY ORDINAL_POSITION ASC'; - } - - /** - * @deprecated Use {@see getColumnTypeSQLSnippet()} instead. - * - * The SQL snippets required to elucidate a column type - * - * Returns an array of the form [column type SELECT snippet, additional JOIN statement snippet] - * - * @return array{string, string} - */ - public function getColumnTypeSQLSnippets(string $tableAlias = 'c'): array - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6202', - 'AbstractMySQLPlatform::getColumnTypeSQLSnippets() is deprecated. ' - . 'Use AbstractMySQLPlatform::getColumnTypeSQLSnippet() instead.', - ); - - return [$this->getColumnTypeSQLSnippet(...func_get_args()), '']; - } - /** * The SQL snippet required to elucidate a column type * * Returns a column type SELECT snippet string */ - public function getColumnTypeSQLSnippet(string $tableAlias = 'c', ?string $databaseName = null): string + public function getColumnTypeSQLSnippet(string $tableAlias, string $databaseName): string { return $tableAlias . '.COLUMN_TYPE'; } - /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ - public function getListTableMetadataSQL(string $table, ?string $database = null): string - { - return sprintf( - <<<'SQL' -SELECT t.ENGINE, - t.AUTO_INCREMENT, - t.TABLE_COMMENT, - t.CREATE_OPTIONS, - t.TABLE_COLLATION, - ccsa.CHARACTER_SET_NAME -FROM information_schema.TABLES t - INNER JOIN information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` ccsa - ON ccsa.COLLATION_NAME = t.TABLE_COLLATION -WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s -SQL - , - $this->getDatabaseNameSQL($database), - $this->quoteStringLiteral($table), - ); - } - /** * {@inheritDoc} */ - public function getCreateTablesSQL(array $tables): array - { - $sql = []; - - foreach ($tables as $table) { - $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); - } - - foreach ($tables as $table) { - if (! $table->hasOption('engine') || $this->engineSupportsForeignKeys($table->getOption('engine'))) { - foreach ($table->getForeignKeys() as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL( - $foreignKey, - $table->getQuotedName($this), - ); - } - } elseif (count($table->getForeignKeys()) > 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5414', - 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' - . ' other than InnoDB is deprecated.' - . ' Define foreign key constraints only if they are necessary.', - ); - } - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $constraintName => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + foreach ($options['uniqueConstraints'] as $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition); } } // add all indexes if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $indexName => $definition) { - $queryFields .= ', ' . $this->getIndexDeclarationSQL($indexName, $definition); + foreach ($options['indexes'] as $definition) { + $queryFields .= ', ' . $this->getIndexDeclarationSQL($definition); } } @@ -511,32 +253,29 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } - $query = 'CREATE '; + $sql = ['CREATE']; if (! empty($options['temporary'])) { - $query .= 'TEMPORARY '; + $sql[] = 'TEMPORARY'; } - $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; - $query .= $this->buildTableOptions($options); - $query .= $this->buildPartitionOptions($options); + $sql[] = 'TABLE ' . $name . ' (' . $queryFields . ')'; - $sql = [$query]; + $tableOptions = $this->buildTableOptions($options); + + if ($tableOptions !== '') { + $sql[] = $tableOptions; + } + + if (isset($options['partition_options'])) { + $sql[] = $options['partition_options']; + } + + $sql = [implode(' ', $sql)]; - // Propagate foreign key constraints only for InnoDB. if (isset($options['foreignKeys'])) { - if (! isset($options['engine']) || $this->engineSupportsForeignKeys($options['engine'])) { - foreach ($options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } elseif (count($options['foreignKeys']) > 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5414', - 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' - . ' other than InnoDB is deprecated.' - . ' Define foreign key constraints only if they are necessary.', - ); + foreach ($options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } @@ -548,21 +287,6 @@ public function createSelectSQLBuilder(): SelectSQLBuilder return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', null); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getDefaultValueDeclarationSQL($column) - { - // Unset the default value if the given column definition does not allow default values. - if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { - $column['default'] = null; - } - - return parent::getDefaultValueDeclarationSQL($column); - } - /** * Build SQL for table options * @@ -576,36 +300,18 @@ private function buildTableOptions(array $options): string $tableOptions = []; - // Charset - if (! isset($options['charset'])) { - $options['charset'] = 'utf8'; - } - - $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); - - if (isset($options['collate'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5214', - 'The "collate" option is deprecated in favor of "collation" and will be removed in 4.0.', - ); - $options['collation'] = $options['collate']; + if (isset($options['charset'])) { + $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); } - // Collation - if (! isset($options['collation'])) { - $options['collation'] = $options['charset'] . '_unicode_ci'; + if (isset($options['collation'])) { + $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collation']); } - $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collation']); - - // Engine - if (! isset($options['engine'])) { - $options['engine'] = 'InnoDB'; + if (isset($options['engine'])) { + $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); } - $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); - // Auto increment if (isset($options['auto_increment'])) { $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); @@ -624,50 +330,17 @@ private function buildTableOptions(array $options): string return implode(' ', $tableOptions); } - /** - * Build SQL for partition options. - * - * @param mixed[] $options - */ - private function buildPartitionOptions(array $options): string - { - return isset($options['partition_options']) - ? ' ' . $options['partition_options'] - : ''; - } - - private function engineSupportsForeignKeys(string $engine): bool - { - return strcasecmp(trim($engine), 'InnoDB') === 0; - } - /** * {@inheritDoc} */ - public function getAlterTableSQL(TableDiff $diff) + public function getAlterTableSQL(TableDiff $diff): array { $columnSql = []; $queryParts = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of SQL that renames a table using %s is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); - } foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - $columnProperties = array_merge($column->toArray(), [ - 'comment' => $this->getColumnComment($column), + 'comment' => $column->getComment(), ]); $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL( @@ -677,39 +350,27 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getDroppedColumns() as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - $queryParts[] = 'DROP ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - $newColumn = $columnDiff->getNewColumn(); $newColumnProperties = array_merge($newColumn->toArray(), [ - 'comment' => $this->getColumnComment($newColumn), + 'comment' => $newColumn->getComment(), ]); - $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + $oldColumn = $columnDiff->getOldColumn(); $queryParts[] = 'CHANGE ' . $oldColumn->getQuotedName($this) . ' ' . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $newColumnProperties); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - $oldColumnName = new Identifier($oldColumnName); $columnProperties = array_merge($column->toArray(), [ - 'comment' => $this->getColumnComment($column), + 'comment' => $column->getComment(), ]); $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' @@ -721,7 +382,7 @@ public function getAlterTableSQL(TableDiff $diff) $diffModified = false; if (isset($addedIndexes['primary'])) { - $keyColumns = array_unique(array_values($addedIndexes['primary']->getColumns())); + $keyColumns = array_values(array_unique($addedIndexes['primary']->getColumns())); $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; unset($addedIndexes['primary']); $diffModified = true; @@ -731,7 +392,7 @@ public function getAlterTableSQL(TableDiff $diff) // Necessary in case the new primary key includes a new auto_increment column foreach ($modifiedIndexes['primary']->getColumns() as $columnName) { if (isset($addedColumns[$columnName]) && $addedColumns[$columnName]->getAutoincrement()) { - $keyColumns = array_unique(array_values($modifiedIndexes['primary']->getColumns())); + $keyColumns = array_values(array_unique($modifiedIndexes['primary']->getColumns())); $queryParts[] = 'DROP PRIMARY KEY'; $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; unset($modifiedIndexes['primary']); @@ -743,49 +404,46 @@ public function getAlterTableSQL(TableDiff $diff) if ($diffModified) { $diff = new TableDiff( - $diff->name, + $diff->getOldTable(), $diff->getAddedColumns(), $diff->getModifiedColumns(), $diff->getDroppedColumns(), + $diff->getRenamedColumns(), array_values($addedIndexes), array_values($modifiedIndexes), $diff->getDroppedIndexes(), - $diff->getOldTable(), + $diff->getRenamedIndexes(), $diff->getAddedForeignKeys(), $diff->getModifiedForeignKeys(), $diff->getDroppedForeignKeys(), - $diff->getRenamedColumns(), - $diff->getRenamedIndexes(), ); } $sql = []; $tableSql = []; - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this) . ' ' - . implode(', ', $queryParts); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff), - ); + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $diff->getOldTable()->getQuotedName($this) . ' ' + . implode(', ', $queryParts); } + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $this->getPostAlterTableIndexForeignKeySQL($diff), + ); + return array_merge($sql, $tableSql, $columnSql); } /** * {@inheritDoc} */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array { $sql = []; - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); foreach ($diff->getModifiedIndexes() as $changedIndex) { $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); @@ -809,7 +467,7 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) $query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', '; $query .= 'ADD ' . $indexClause; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addedIndex) . ')'; + $query .= ' (' . implode(', ', $addedIndex->getQuotedColumns($this)) . ')'; $sql[] = $query; @@ -820,33 +478,16 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) } } - $engine = 'INNODB'; - - $table = $diff->getOldTable(); - - if ($table !== null && $table->hasOption('engine')) { - $engine = strtoupper(trim($table->getOption('engine'))); - } - - // Suppress foreign key constraint propagation on non-supporting engines. - if ($engine !== 'INNODB') { - $diff->addedForeignKeys = []; - $diff->changedForeignKeys = []; - $diff->removedForeignKeys = []; - } - - $sql = array_merge( + return array_merge( $sql, $this->getPreAlterTableAlterIndexForeignKeySQL($diff), parent::getPreAlterTableIndexForeignKeySQL($diff), $this->getPreAlterTableRenameIndexForeignKeySQL($diff), ); - - return $sql; } /** - * @return string[] + * @return list * * @throws Exception */ @@ -858,13 +499,9 @@ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $inde $table = $diff->getOldTable(); - if ($table === null) { - return []; - } - $sql = []; - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $tableNameSQL = $table->getQuotedName($this); // Dropping primary keys requires to unset autoincrement attribute on the particular column first. foreach ($index->getColumns() as $columnName) { @@ -874,7 +511,7 @@ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $inde $column = $table->getColumn($columnName); - if ($column->getAutoincrement() !== true) { + if (! $column->getAutoincrement()) { continue; } @@ -893,7 +530,7 @@ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $inde /** * @param TableDiff $diff The table diff to gather the SQL for. * - * @return string[] + * @return list * * @throws Exception */ @@ -901,10 +538,6 @@ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array { $table = $diff->getOldTable(); - if ($table === null) { - return []; - } - $primaryKey = $table->getPrimaryKey(); if ($primaryKey === null) { @@ -960,109 +593,14 @@ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array /** * @param TableDiff $diff The table diff to gather the SQL for. * - * @return string[] + * @return list */ - protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) + protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff): array { - $sql = []; - - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { - continue; - } - - $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); - } - - return $sql; + return []; } - /** - * Returns the remaining foreign key constraints that require one of the renamed indexes. - * - * "Remaining" here refers to the diff between the foreign keys currently defined in the associated - * table and the foreign keys to be removed. - * - * @param TableDiff $diff The table diff to evaluate. - * - * @return ForeignKeyConstraint[] - */ - private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff): array - { - if (count($diff->getRenamedIndexes()) === 0) { - return []; - } - - $table = $diff->getOldTable(); - - if ($table === null) { - return []; - } - - $foreignKeys = []; - /** @var ForeignKeyConstraint[] $remainingForeignKeys */ - $remainingForeignKeys = array_diff_key( - $table->getForeignKeys(), - $diff->getDroppedForeignKeys(), - ); - - foreach ($remainingForeignKeys as $foreignKey) { - foreach ($diff->getRenamedIndexes() as $index) { - if ($foreignKey->intersectsIndexColumns($index)) { - $foreignKeys[] = $foreignKey; - - break; - } - } - } - - return $foreignKeys; - } - - /** - * {@inheritDoc} - */ - protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) - { - return array_merge( - parent::getPostAlterTableIndexForeignKeySQL($diff), - $this->getPostAlterTableRenameIndexForeignKeySQL($diff), - ); - } - - /** - * @param TableDiff $diff The table diff to gather the SQL for. - * - * @return string[] - */ - protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $tableNameSQL = $newName->getQuotedName($this); - } else { - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - } - - foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { - if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { - continue; - } - - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) + protected function getCreateIndexSQLFlags(Index $index): string { $type = ''; if ($index->isUnique()) { @@ -1079,7 +617,7 @@ protected function getCreateIndexSQLFlags(Index $index) /** * {@inheritDoc} */ - public function getIntegerTypeDeclarationSQL(array $column) + public function getIntegerTypeDeclarationSQL(array $column): string { return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1087,7 +625,7 @@ public function getIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBigIntTypeDeclarationSQL(array $column) + public function getBigIntTypeDeclarationSQL(array $column): string { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1095,7 +633,7 @@ public function getBigIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getSmallIntTypeDeclarationSQL(array $column) + public function getSmallIntTypeDeclarationSQL(array $column): string { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1103,7 +641,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getFloatDeclarationSQL(array $column) + public function getFloatDeclarationSQL(array $column): string { return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); } @@ -1111,7 +649,7 @@ public function getFloatDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDecimalTypeDeclarationSQL(array $column) + public function getDecimalTypeDeclarationSQL(array $column): string { return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); } @@ -1129,7 +667,7 @@ private function getUnsignedDeclaration(array $columnDef): string /** * {@inheritDoc} */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string { $autoinc = ''; if (! empty($column['autoincrement'])) { @@ -1139,22 +677,14 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) return $this->getUnsignedDeclaration($column) . $autoinc; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getColumnCharsetDeclarationSQL($charset) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getColumnCharsetDeclarationSQL(string $charset): string { return 'CHARACTER SET ' . $charset; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string { $query = ''; if ($foreignKey->hasOption('match')) { @@ -1166,60 +696,9 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey return $query; } - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) + public function getDropIndexSQL(string $name, string $table): string { - if ($index instanceof Index) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $indexName = $index->getQuotedName($this); - } elseif (is_string($index)) { - $indexName = $index; - } else { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', - ); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', - ); - } - - if ($index instanceof Index && $index->isPrimary()) { - // MySQL 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; - } - - /** - * @param string $table - * - * @return string - */ - protected function getDropPrimaryKeySQL($table) - { - return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; + return 'DROP INDEX ' . $name . ' ON ' . $table; } /** @@ -1232,40 +711,12 @@ public function getDropUniqueConstraintSQL(string $name, string $tableName): str return $this->getDropIndexSQL($name, $tableName); } - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string { return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } - /** - * {@inheritDoc} - */ - public function getName() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4749', - 'AbstractMySQLPlatform::getName() is deprecated. Identify platforms by their class.', - ); - - return 'mysql'; - } - - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'LOCK IN SHARE MODE'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings(): void { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, @@ -1279,6 +730,7 @@ protected function initializeDoctrineTypeMappings() 'float' => Types::FLOAT, 'int' => Types::INTEGER, 'integer' => Types::INTEGER, + 'json' => Types::JSON, 'longblob' => Types::BLOB, 'longtext' => Types::TEXT, 'mediumblob' => Types::BLOB, @@ -1301,53 +753,9 @@ protected function initializeDoctrineTypeMappings() ]; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getVarcharMaxLength() + protected function createReservedKeywordsList(): KeywordList { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractMySQLPlatform::getVarcharMaxLength() is deprecated.', - ); - - return 65535; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getBinaryMaxLength() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractMySQLPlatform::getBinaryMaxLength() is deprecated.', - ); - - return 65535; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'AbstractMySQLPlatform::getReservedKeywordsClass() is deprecated,' - . ' use AbstractMySQLPlatform::createReservedKeywordsList() instead.', - ); - - return Keywords\MySQLKeywords::class; + return new MySQLKeywords(); } /** @@ -1356,23 +764,8 @@ protected function getReservedKeywordsClass() * MySQL commits a transaction implicitly when DROP TABLE is executed, however not * if DROP TEMPORARY TABLE is executed. */ - public function getDropTemporaryTableSQL($table) + public function getDropTemporaryTableSQL(string $table): string { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', - ); - } - return 'DROP TEMPORARY TABLE ' . $table; } @@ -1385,7 +778,7 @@ public function getDropTemporaryTableSQL($table) * * {@inheritDoc} */ - public function getBlobTypeDeclarationSQL(array $column) + public function getBlobTypeDeclarationSQL(array $column): string { if (! empty($column['length']) && is_numeric($column['length'])) { $length = $column['length']; @@ -1406,20 +799,15 @@ public function getBlobTypeDeclarationSQL(array $column) return 'LONGBLOB'; } - /** - * {@inheritDoc} - */ - public function quoteStringLiteral($str) + public function quoteStringLiteral(string $str): string { - $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped + // MySQL requires backslashes to be escaped as well. + $str = str_replace('\\', '\\\\', $str); return parent::quoteStringLiteral($str); } - /** - * {@inheritDoc} - */ - public function getDefaultTransactionIsolationLevel() + public function getDefaultTransactionIsolationLevel(): TransactionIsolationLevel { return TransactionIsolationLevel::REPEATABLE_READ; } @@ -1429,30 +817,13 @@ public function supportsColumnLengthIndexes(): bool return true; } - /** @deprecated Will be removed without replacement. */ - protected function getDatabaseNameSQL(?string $databaseName): string - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6215', - '%s is deprecated without replacement.', - __METHOD__, - ); - - if ($databaseName !== null) { - return $this->quoteStringLiteral($databaseName); - } - - return $this->getCurrentDatabaseExpression(); - } - public function createSchemaManager(Connection $connection): MySQLSchemaManager { return new MySQLSchemaManager($connection, $this); } /** - * @param list $assets + * @param array $assets * * @return array * diff --git a/doctrine/dbal/src/Platforms/AbstractPlatform.php b/doctrine/dbal/src/Platforms/AbstractPlatform.php index 49e131552..0beb37a09 100644 --- a/doctrine/dbal/src/Platforms/AbstractPlatform.php +++ b/doctrine/dbal/src/Platforms/AbstractPlatform.php @@ -1,26 +1,23 @@ disableTypeComments = $value; - } - - /** - * Sets the EventManager used by the Platform. - * - * @deprecated - * - * @return void - */ - public function setEventManager(EventManager $eventManager) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - '%s is deprecated.', - __METHOD__, - ); - - $this->_eventManager = $eventManager; - } - - /** - * Gets the EventManager used by the Platform. - * - * @deprecated - * - * @return EventManager|null */ - public function getEventManager() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - '%s is deprecated.', - __METHOD__, - ); - - return $this->_eventManager; - } + protected ?KeywordList $_keywords = null; /** * Returns the SQL snippet that declares a boolean column. * * @param mixed[] $column - * - * @return string */ - abstract public function getBooleanTypeDeclarationSQL(array $column); + abstract public function getBooleanTypeDeclarationSQL(array $column): string; /** * Returns the SQL snippet that declares a 4 byte integer column. * * @param mixed[] $column - * - * @return string */ - abstract public function getIntegerTypeDeclarationSQL(array $column); + abstract public function getIntegerTypeDeclarationSQL(array $column): string; /** * Returns the SQL snippet that declares an 8 byte integer column. * * @param mixed[] $column - * - * @return string */ - abstract public function getBigIntTypeDeclarationSQL(array $column); + abstract public function getBigIntTypeDeclarationSQL(array $column): string; /** * Returns the SQL snippet that declares a 2 byte integer column. * * @param mixed[] $column - * - * @return string */ - abstract public function getSmallIntTypeDeclarationSQL(array $column); + abstract public function getSmallIntTypeDeclarationSQL(array $column): string; /** * Returns the SQL snippet that declares common properties of an integer column. * * @param mixed[] $column - * - * @return string */ - abstract protected function _getCommonIntegerTypeDeclarationSQL(array $column); + abstract protected function _getCommonIntegerTypeDeclarationSQL(array $column): string; /** * Lazy load Doctrine Type Mappings. - * - * @return void */ - abstract protected function initializeDoctrineTypeMappings(); + abstract protected function initializeDoctrineTypeMappings(): void; /** * Initializes Doctrine Type Mappings with the platform defaults @@ -223,7 +141,7 @@ private function initializeAllDoctrineTypeMappings(): void * Returns the SQL snippet used to declare a column that can * store characters in the ASCII character set * - * @param mixed[] $column + * @param array $column The column definition. */ public function getAsciiStringTypeDeclarationSQL(array $column): string { @@ -231,84 +149,43 @@ public function getAsciiStringTypeDeclarationSQL(array $column): string } /** - * Returns the SQL snippet used to declare a VARCHAR column type. - * - * @deprecated Use {@link getStringTypeDeclarationSQL()} instead. - * - * @param mixed[] $column + * Returns the SQL snippet used to declare a string column type. * - * @return string + * @param array $column The column definition. */ - public function getVarcharTypeDeclarationSQL(array $column) + public function getStringTypeDeclarationSQL(array $column): string { - if (isset($column['length'])) { - $lengthOmitted = false; - } else { - $column['length'] = $this->getVarcharDefaultLength(); - $lengthOmitted = true; - } - - $fixed = $column['fixed'] ?? false; + $length = $column['length'] ?? null; - $maxLength = $fixed - ? $this->getCharMaxLength() - : $this->getVarcharMaxLength(); - - if ($column['length'] > $maxLength) { - return $this->getClobTypeDeclarationSQL($column); + if (empty($column['fixed'])) { + try { + return $this->getVarcharTypeDeclarationSQLSnippet($length); + } catch (InvalidColumnType $e) { + throw InvalidColumnDeclaration::fromInvalidColumnType($column['name'], $e); + } } - return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); - } - - /** - * Returns the SQL snippet used to declare a string column type. - * - * @param mixed[] $column - * - * @return string - */ - public function getStringTypeDeclarationSQL(array $column) - { - return $this->getVarcharTypeDeclarationSQL($column); + return $this->getCharTypeDeclarationSQLSnippet($length); } /** - * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. - * - * @param mixed[] $column The column definition. + * Returns the SQL snippet used to declare a binary string column type. * - * @return string + * @param array $column The column definition. */ - public function getBinaryTypeDeclarationSQL(array $column) + public function getBinaryTypeDeclarationSQL(array $column): string { - if (isset($column['length'])) { - $lengthOmitted = false; - } else { - $column['length'] = $this->getBinaryDefaultLength(); - $lengthOmitted = true; - } - - $fixed = $column['fixed'] ?? false; - - $maxLength = $this->getBinaryMaxLength(); + $length = $column['length'] ?? null; - if ($column['length'] > $maxLength) { - if ($maxLength > 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3187', - 'Binary column length %d is greater than supported by the platform (%d).' - . ' Reduce the column length or use a BLOB column instead.', - $column['length'], - $maxLength, - ); + try { + if (empty($column['fixed'])) { + return $this->getVarbinaryTypeDeclarationSQLSnippet($length); } - return $this->getBlobTypeDeclarationSQL($column); + return $this->getBinaryTypeDeclarationSQLSnippet($length); + } catch (InvalidColumnType $e) { + throw InvalidColumnDeclaration::fromInvalidColumnType($column['name'], $e); } - - return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); } /** @@ -317,11 +194,9 @@ public function getBinaryTypeDeclarationSQL(array $column) * By default this maps directly to a CHAR(36) and only maps to more * special datatypes when the underlying databases support this datatype. * - * @param mixed[] $column - * - * @return string + * @param array $column The column definition. */ - public function getGuidTypeDeclarationSQL(array $column) + public function getGuidTypeDeclarationSQL(array $column): string { $column['length'] = 36; $column['fixed'] = true; @@ -336,111 +211,109 @@ public function getGuidTypeDeclarationSQL(array $column) * special datatypes when the underlying databases support this datatype. * * @param mixed[] $column - * - * @return string */ - public function getJsonTypeDeclarationSQL(array $column) + public function getJsonTypeDeclarationSQL(array $column): string { return $this->getClobTypeDeclarationSQL($column); } /** - * @param int|false $length - * @param bool $fixed - * - * @return string - * - * @throws Exception If not supported on this platform. + * @param int|null $length The length of the column in characters + * or NULL if the length should be omitted. */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + protected function getCharTypeDeclarationSQLSnippet(?int $length): string { - throw Exception::notSupported('VARCHARs not supported by Platform.'); + $sql = 'CHAR'; + + if ($length !== null) { + $sql .= sprintf('(%d)', $length); + } + + return $sql; } /** - * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. - * - * @param int|false $length The length of the column. - * @param bool $fixed Whether the column length is fixed. - * - * @return string - * - * @throws Exception If not supported on this platform. + * @param int|null $length The length of the column in characters + * or NULL if the length should be omitted. */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string { - throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); + if ($length === null) { + throw ColumnLengthRequired::new($this, 'VARCHAR'); + } + + return sprintf('VARCHAR(%d)', $length); } /** - * Returns the SQL snippet used to declare a CLOB column type. + * Returns the SQL snippet used to declare a fixed length binary column type. * - * @param mixed[] $column + * @param int|null $length The length of the column in bytes + * or NULL if the length should be omitted. + */ + protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string + { + $sql = 'BINARY'; + + if ($length !== null) { + $sql .= sprintf('(%d)', $length); + } + + return $sql; + } + + /** + * Returns the SQL snippet used to declare a variable length binary column type. * - * @return string + * @param int|null $length The length of the column in bytes + * or NULL if the length should be omitted. */ - abstract public function getClobTypeDeclarationSQL(array $column); + protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string + { + if ($length === null) { + throw ColumnLengthRequired::new($this, 'VARBINARY'); + } + + return sprintf('VARBINARY(%d)', $length); + } /** - * Returns the SQL Snippet used to declare a BLOB column type. + * Returns the SQL snippet used to declare a CLOB column type. * * @param mixed[] $column - * - * @return string */ - abstract public function getBlobTypeDeclarationSQL(array $column); + abstract public function getClobTypeDeclarationSQL(array $column): string; /** - * Gets the name of the platform. - * - * @deprecated Identify platforms by their class. + * Returns the SQL Snippet used to declare a BLOB column type. * - * @return string + * @param mixed[] $column */ - abstract public function getName(); + abstract public function getBlobTypeDeclarationSQL(array $column): string; /** * Registers a doctrine type to be used in conjunction with a column type of this platform. * - * @param string $dbType - * @param string $doctrineType - * - * @return void - * * @throws Exception If the type is not found. */ - public function registerDoctrineTypeMapping($dbType, $doctrineType) + public function registerDoctrineTypeMapping(string $dbType, string $doctrineType): void { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); } if (! Types\Type::hasType($doctrineType)) { - throw Exception::typeNotFound($doctrineType); + throw TypeNotFound::new($doctrineType); } $dbType = strtolower($dbType); $this->doctrineTypeMapping[$dbType] = $doctrineType; - - $doctrineType = Type::getType($doctrineType); - - if (! $doctrineType->requiresSQLCommentHint($this)) { - return; - } - - $this->markDoctrineTypeCommented($doctrineType); } /** * Gets the Doctrine type that is mapped for the given database column type. - * - * @param string $dbType - * - * @return string - * - * @throws Exception */ - public function getDoctrineTypeMapping($dbType) + public function getDoctrineTypeMapping(string $dbType): string { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); @@ -449,9 +322,11 @@ public function getDoctrineTypeMapping($dbType) $dbType = strtolower($dbType); if (! isset($this->doctrineTypeMapping[$dbType])) { - throw new Exception( - 'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.', - ); + throw new InvalidArgumentException(sprintf( + 'Unknown database type "%s" requested, %s may not support it.', + $dbType, + static::class, + )); } return $this->doctrineTypeMapping[$dbType]; @@ -459,12 +334,8 @@ public function getDoctrineTypeMapping($dbType) /** * Checks if a database type is currently supported by this platform. - * - * @param string $dbType - * - * @return bool */ - public function hasDoctrineTypeMappingFor($dbType) + public function hasDoctrineTypeMappingFor(string $dbType): bool { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); @@ -476,3849 +347,1629 @@ public function hasDoctrineTypeMappingFor($dbType) } /** - * Initializes the Doctrine Type comments instance variable for in_array() checks. - * - * @deprecated This API will be removed in Doctrine DBAL 4.0. - * - * @return void + * Returns the regular expression operator. */ - protected function initializeCommentedDoctrineTypes() + public function getRegexpExpression(): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5058', - '%s is deprecated and will be removed in Doctrine DBAL 4.0.', - __METHOD__, - ); - - $this->doctrineTypeComments = []; - - foreach (Type::getTypesMap() as $typeName => $className) { - $type = Type::getType($typeName); - - if (! $type->requiresSQLCommentHint($this)) { - continue; - } - - $this->doctrineTypeComments[] = $typeName; - } + throw NotSupported::new(__METHOD__); } /** - * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? - * - * @deprecated Use {@link Type::requiresSQLCommentHint()} instead. + * Returns the SQL snippet to get the length of a text column in characters. * - * @return bool + * @param string $string SQL expression producing the string. */ - public function isCommentedDoctrineType(Type $doctrineType) + public function getLengthExpression(string $string): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5058', - '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__, - ); - - if ($this->doctrineTypeComments === null) { - $this->initializeCommentedDoctrineTypes(); - } - - return $doctrineType->requiresSQLCommentHint($this); + return 'LENGTH(' . $string . ')'; } /** - * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements. - * - * @param string|Type $doctrineType + * Returns the SQL snippet to get the remainder of the operation of division of dividend by divisor. * - * @return void + * @param string $dividend SQL expression producing the dividend. + * @param string $divisor SQL expression producing the divisor. */ - public function markDoctrineTypeCommented($doctrineType) + public function getModExpression(string $dividend, string $divisor): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5058', - '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__, - ); + return 'MOD(' . $dividend . ', ' . $divisor . ')'; + } + + /** + * Returns the SQL snippet to trim a string. + * + * @param string $str The expression to apply the trim to. + * @param TrimMode $mode The position of the trim. + * @param string|null $char The char to trim, has to be quoted already. Defaults to space. + */ + public function getTrimExpression( + string $str, + TrimMode $mode = TrimMode::UNSPECIFIED, + ?string $char = null, + ): string { + $tokens = []; - if ($this->doctrineTypeComments === null) { - $this->initializeCommentedDoctrineTypes(); + switch ($mode) { + case TrimMode::UNSPECIFIED: + break; + + case TrimMode::LEADING: + $tokens[] = 'LEADING'; + break; + + case TrimMode::TRAILING: + $tokens[] = 'TRAILING'; + break; + + case TrimMode::BOTH: + $tokens[] = 'BOTH'; + break; + } + + if ($char !== null) { + $tokens[] = $char; + } + + if (count($tokens) > 0) { + $tokens[] = 'FROM'; } - assert(is_array($this->doctrineTypeComments)); + $tokens[] = $str; - $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; + return sprintf('TRIM(%s)', implode(' ', $tokens)); } /** - * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. - * - * @deprecated This method will be removed without replacement. + * Returns the SQL snippet to get the position of the first occurrence of the substring in the string. * - * @return string + * @param string $string SQL expression producing the string to locate the substring in. + * @param string $substring SQL expression producing the substring to locate. + * @param string|null $start SQL expression producing the position to start at. + * Defaults to the beginning of the string. */ - public function getDoctrineTypeComment(Type $doctrineType) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5107', - '%s is deprecated and will be removed in Doctrine DBAL 4.0.', - __METHOD__, - ); - - return '(DC2Type:' . $doctrineType->getName() . ')'; - } + abstract public function getLocateExpression(string $string, string $substring, ?string $start = null): string; /** - * Gets the comment of a passed column modified by potential doctrine type comment hints. + * Returns an SQL snippet to get a substring inside the string. * - * @deprecated This method will be removed without replacement. + * Note: Not SQL92, but common functionality. * - * @return string|null + * @param string $string SQL expression producing the string from which a substring should be extracted. + * @param string $start SQL expression producing the position to start at, + * @param string|null $length SQL expression producing the length of the substring portion to be returned. + * By default, the entire substring is returned. */ - protected function getColumnComment(Column $column) + public function getSubstringExpression(string $string, string $start, ?string $length = null): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5107', - '%s is deprecated and will be removed in Doctrine DBAL 4.0.', - __METHOD__, - ); - - $comment = $column->getComment(); - - if (! $this->disableTypeComments && $column->getType()->requiresSQLCommentHint($this)) { - $comment .= $this->getDoctrineTypeComment($column->getType()); + if ($length === null) { + return sprintf('SUBSTRING(%s FROM %s)', $string, $start); } - return $comment; + return sprintf('SUBSTRING(%s FROM %s FOR %s)', $string, $start, $length); } /** - * Gets the character used for identifier quoting. - * - * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead. - * - * @return string + * Returns a SQL snippet to concatenate the given strings. */ - public function getIdentifierQuoteCharacter() + public function getConcatExpression(string ...$string): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5388', - 'AbstractPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.', - ); - - return '"'; + return implode(' || ', $string); } /** - * Gets the string portion that starts an SQL comment. + * Returns the SQL to calculate the difference in days between the two passed dates. * - * @deprecated + * Computes diff = date1 - date2. + */ + abstract public function getDateDiffExpression(string $date1, string $date2): string; + + /** + * Returns the SQL to add the number of given seconds to a date. * - * @return string + * @param string $date SQL expression producing the date. + * @param string $seconds SQL expression producing the number of seconds. */ - public function getSqlCommentStartString() + public function getDateAddSecondsExpression(string $date, string $seconds): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getSqlCommentStartString() is deprecated.', - ); - - return '--'; + return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND); } /** - * Gets the string portion that ends an SQL comment. - * - * @deprecated + * Returns the SQL to subtract the number of given seconds from a date. * - * @return string + * @param string $date SQL expression producing the date. + * @param string $seconds SQL expression producing the number of seconds. */ - public function getSqlCommentEndString() + public function getDateSubSecondsExpression(string $date, string $seconds): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getSqlCommentEndString() is deprecated.', - ); - - return "\n"; + return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND); } /** - * Gets the maximum length of a char column. + * Returns the SQL to add the number of given minutes to a date. * - * @deprecated + * @param string $date SQL expression producing the date. + * @param string $minutes SQL expression producing the number of minutes. */ - public function getCharMaxLength(): int + public function getDateAddMinutesExpression(string $date, string $minutes): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getCharMaxLength() is deprecated.', - ); - - return $this->getVarcharMaxLength(); + return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE); } /** - * Gets the maximum length of a varchar column. - * - * @deprecated + * Returns the SQL to subtract the number of given minutes from a date. * - * @return int + * @param string $date SQL expression producing the date. + * @param string $minutes SQL expression producing the number of minutes. */ - public function getVarcharMaxLength() + public function getDateSubMinutesExpression(string $date, string $minutes): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getVarcharMaxLength() is deprecated.', - ); - - return 4000; + return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE); } /** - * Gets the default length of a varchar column. - * - * @deprecated + * Returns the SQL to add the number of given hours to a date. * - * @return int + * @param string $date SQL expression producing the date. + * @param string $hours SQL expression producing the number of hours. */ - public function getVarcharDefaultLength() + public function getDateAddHourExpression(string $date, string $hours): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default varchar column length is deprecated, specify the length explicitly.', - ); - - return 255; + return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR); } /** - * Gets the maximum length of a binary column. - * - * @deprecated + * Returns the SQL to subtract the number of given hours to a date. * - * @return int + * @param string $date SQL expression producing the date. + * @param string $hours SQL expression producing the number of hours. */ - public function getBinaryMaxLength() + public function getDateSubHourExpression(string $date, string $hours): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'AbstractPlatform::getBinaryMaxLength() is deprecated.', - ); - - return 4000; + return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR); } /** - * Gets the default length of a binary column. - * - * @deprecated + * Returns the SQL to add the number of given days to a date. * - * @return int + * @param string $date SQL expression producing the date. + * @param string $days SQL expression producing the number of days. */ - public function getBinaryDefaultLength() + public function getDateAddDaysExpression(string $date, string $days): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length is deprecated, specify the length explicitly.', - ); - - return 255; + return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY); } /** - * Gets all SQL wildcard characters of the platform. - * - * @deprecated Use {@see AbstractPlatform::getLikeWildcardCharacters()} instead. + * Returns the SQL to subtract the number of given days to a date. * - * @return string[] + * @param string $date SQL expression producing the date. + * @param string $days SQL expression producing the number of days. */ - public function getWildcards() + public function getDateSubDaysExpression(string $date, string $days): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getWildcards() is deprecated.' - . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.', - ); - - return ['%', '_']; + return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY); } /** - * Returns the regular expression operator. - * - * @return string + * Returns the SQL to add the number of given weeks to a date. * - * @throws Exception If not supported on this platform. + * @param string $date SQL expression producing the date. + * @param string $weeks SQL expression producing the number of weeks. */ - public function getRegexpExpression() + public function getDateAddWeeksExpression(string $date, string $weeks): string { - throw Exception::notSupported(__METHOD__); + return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK); } /** - * Returns the SQL snippet to get the average value of a column. - * - * @deprecated Use AVG() in SQL instead. - * - * @param string $column The column to use. + * Returns the SQL to subtract the number of given weeks from a date. * - * @return string Generated SQL including an AVG aggregate function. + * @param string $date SQL expression producing the date. + * @param string $weeks SQL expression producing the number of weeks. */ - public function getAvgExpression($column) + public function getDateSubWeeksExpression(string $date, string $weeks): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.', - ); - - return 'AVG(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK); } /** - * Returns the SQL snippet to get the number of rows (without a NULL value) of a column. - * - * If a '*' is used instead of a column the number of selected rows is returned. - * - * @deprecated Use COUNT() in SQL instead. - * - * @param string|int $column The column to use. + * Returns the SQL to add the number of given months to a date. * - * @return string Generated SQL including a COUNT aggregate function. + * @param string $date SQL expression producing the date. + * @param string $months SQL expression producing the number of months. */ - public function getCountExpression($column) + public function getDateAddMonthExpression(string $date, string $months): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.', - ); - - return 'COUNT(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH); } /** - * Returns the SQL snippet to get the highest value of a column. - * - * @deprecated Use MAX() in SQL instead. - * - * @param string $column The column to use. + * Returns the SQL to subtract the number of given months to a date. * - * @return string Generated SQL including a MAX aggregate function. + * @param string $date SQL expression producing the date. + * @param string $months SQL expression producing the number of months. */ - public function getMaxExpression($column) + public function getDateSubMonthExpression(string $date, string $months): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.', - ); - - return 'MAX(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH); } /** - * Returns the SQL snippet to get the lowest value of a column. - * - * @deprecated Use MIN() in SQL instead. - * - * @param string $column The column to use. + * Returns the SQL to add the number of given quarters to a date. * - * @return string Generated SQL including a MIN aggregate function. + * @param string $date SQL expression producing the date. + * @param string $quarters SQL expression producing the number of quarters. */ - public function getMinExpression($column) + public function getDateAddQuartersExpression(string $date, string $quarters): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.', - ); - - return 'MIN(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER); } /** - * Returns the SQL snippet to get the total sum of a column. - * - * @deprecated Use SUM() in SQL instead. - * - * @param string $column The column to use. + * Returns the SQL to subtract the number of given quarters from a date. * - * @return string Generated SQL including a SUM aggregate function. + * @param string $date SQL expression producing the date. + * @param string $quarters SQL expression producing the number of quarters. */ - public function getSumExpression($column) + public function getDateSubQuartersExpression(string $date, string $quarters): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.', - ); - - return 'SUM(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER); } - // scalar functions - /** - * Returns the SQL snippet to get the md5 sum of a column. - * - * Note: Not SQL92, but common functionality. - * - * @deprecated + * Returns the SQL to add the number of given years to a date. * - * @param string $column - * - * @return string + * @param string $date SQL expression producing the date. + * @param string $years SQL expression producing the number of years. */ - public function getMd5Expression($column) + public function getDateAddYearsExpression(string $date, string $years): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getMd5Expression() is deprecated.', - ); - - return 'MD5(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR); } /** - * Returns the SQL snippet to get the length of a text column in characters. - * - * @param string $column + * Returns the SQL to subtract the number of given years from a date. * - * @return string + * @param string $date SQL expression producing the date. + * @param string $years SQL expression producing the number of years. */ - public function getLengthExpression($column) + public function getDateSubYearsExpression(string $date, string $years): string { - return 'LENGTH(' . $column . ')'; + return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR); } /** - * Returns the SQL snippet to get the squared value of a column. - * - * @deprecated Use SQRT() in SQL instead. - * - * @param string $column The column to use. + * Returns the SQL for a date arithmetic expression. * - * @return string Generated SQL including an SQRT aggregate function. + * @param string $date SQL expression representing a date to perform the arithmetic operation on. + * @param string $operator The arithmetic operator (+ or -). + * @param string $interval SQL expression representing the value of the interval that shall be calculated + * into the date. + * @param DateIntervalUnit $unit The unit of the interval that shall be calculated into the date. */ - public function getSqrtExpression($column) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.', - ); - - return 'SQRT(' . $column . ')'; - } + abstract protected function getDateArithmeticIntervalExpression( + string $date, + string $operator, + string $interval, + DateIntervalUnit $unit, + ): string; /** - * Returns the SQL snippet to round a numeric column to the number of decimals specified. - * - * @deprecated Use ROUND() in SQL instead. - * - * @param string $column - * @param string|int $decimals + * Generates the SQL expression which represents the given date interval multiplied by a number * - * @return string + * @param string $interval SQL expression describing the interval value + * @param int $multiplier Interval multiplier */ - public function getRoundExpression($column, $decimals = 0) + protected function multiplyInterval(string $interval, int $multiplier): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.', - ); - - return 'ROUND(' . $column . ', ' . $decimals . ')'; + return sprintf('(%s * %d)', $interval, $multiplier); } /** - * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2. - * - * @param string $expression1 - * @param string $expression2 + * Returns the SQL bit AND comparison expression. * - * @return string + * @param string $value1 SQL expression producing the first value. + * @param string $value2 SQL expression producing the second value. */ - public function getModExpression($expression1, $expression2) + public function getBitAndComparisonExpression(string $value1, string $value2): string { - return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; + return '(' . $value1 . ' & ' . $value2 . ')'; } /** - * Returns the SQL snippet to trim a string. - * - * @param string $str The expression to apply the trim to. - * @param int $mode The position of the trim (leading/trailing/both). - * @param string|bool $char The char to trim, has to be quoted already. Defaults to space. + * Returns the SQL bit OR comparison expression. * - * @return string + * @param string $value1 SQL expression producing the first value. + * @param string $value2 SQL expression producing the second value. */ - public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) + public function getBitOrComparisonExpression(string $value1, string $value2): string { - $expression = ''; - - switch ($mode) { - case TrimMode::LEADING: - $expression = 'LEADING '; - break; - - case TrimMode::TRAILING: - $expression = 'TRAILING '; - break; - - case TrimMode::BOTH: - $expression = 'BOTH '; - break; - } - - if ($char !== false) { - $expression .= $char . ' '; - } - - if ($mode !== TrimMode::UNSPECIFIED || $char !== false) { - $expression .= 'FROM '; - } - - return 'TRIM(' . $expression . $str . ')'; + return '(' . $value1 . ' | ' . $value2 . ')'; } /** - * Returns the SQL snippet to trim trailing space characters from the expression. - * - * @deprecated Use RTRIM() in SQL instead. - * - * @param string $str Literal string or column name. - * - * @return string + * Returns the SQL expression which represents the currently selected database. */ - public function getRtrimExpression($str) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.', - ); - - return 'RTRIM(' . $str . ')'; - } + abstract public function getCurrentDatabaseExpression(): string; /** - * Returns the SQL snippet to trim leading space characters from the expression. - * - * @deprecated Use LTRIM() in SQL instead. - * - * @param string $str Literal string or column name. + * Honors that some SQL vendors such as MsSql use table hints for locking instead of the + * ANSI SQL FOR UPDATE specification. * - * @return string + * @param string $fromClause The FROM clause to append the hint for the given lock mode to */ - public function getLtrimExpression($str) + public function appendLockHint(string $fromClause, LockMode $lockMode): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.', - ); - - return 'LTRIM(' . $str . ')'; + return $fromClause; } /** - * Returns the SQL snippet to change all characters from the expression to uppercase, - * according to the current character set mapping. - * - * @deprecated Use UPPER() in SQL instead. - * - * @param string $str Literal string or column name. - * - * @return string + * Returns the SQL snippet to drop an existing table. */ - public function getUpperExpression($str) + public function getDropTableSQL(string $table): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.', - ); - - return 'UPPER(' . $str . ')'; + return 'DROP TABLE ' . $table; } /** - * Returns the SQL snippet to change all characters from the expression to lowercase, - * according to the current character set mapping. - * - * @deprecated Use LOWER() in SQL instead. - * - * @param string $str Literal string or column name. - * - * @return string + * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. */ - public function getLowerExpression($str) + public function getDropTemporaryTableSQL(string $table): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.', - ); - - return 'LOWER(' . $str . ')'; + return $this->getDropTableSQL($table); } /** - * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. - * - * @param string $str Literal string. - * @param string $substr Literal string to find. - * @param string|int|false $startPos Position to start at, beginning of string by default. - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to drop an index from a table. */ - public function getLocateExpression($str, $substr, $startPos = false) + public function getDropIndexSQL(string $name, string $table): string { - throw Exception::notSupported(__METHOD__); + return 'DROP INDEX ' . $name; } /** - * Returns the SQL snippet to get the current system date. - * - * @deprecated Generate dates within the application. + * Returns the SQL to drop a constraint. * - * @return string + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getNowExpression() + protected function getDropConstraintSQL(string $name, string $table): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4753', - 'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.', - ); - - return 'NOW()'; + return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name; } /** - * Returns a SQL snippet to get a substring inside an SQL statement. - * - * Note: Not SQL92, but common functionality. - * - * SQLite only supports the 2 parameter variant of this function. - * - * @param string $string An sql string literal or column name/alias. - * @param string|int $start Where to start the substring portion. - * @param string|int|null $length The substring portion length. - * - * @return string + * Returns the SQL to drop a foreign key. */ - public function getSubstringExpression($string, $start, $length = null) + public function getDropForeignKeySQL(string $foreignKey, string $table): string { - if ($length === null) { - return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; - } - - return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; + return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; } /** - * Returns a SQL snippet to concatenate the given expressions. - * - * Accepts an arbitrary number of string parameters. Each parameter must contain an expression. - * - * @return string + * Returns the SQL to drop a unique constraint. */ - public function getConcatExpression() + public function getDropUniqueConstraintSQL(string $name, string $tableName): string { - return implode(' || ', func_get_args()); + return $this->getDropConstraintSQL($name, $tableName); } /** - * Returns the SQL for a logical not. - * - * Example: - * - * $q = new Doctrine_Query(); - * $e = $q->expr; - * $q->select('*')->from('table') - * ->where($e->eq('id', $e->not('null')); - * - * - * @deprecated Use NOT() in SQL instead. - * - * @param string $expression + * Returns the SQL statement(s) to create a table with the specified name, columns and constraints + * on this platform. * - * @return string The logical expression. + * @return list The list of SQL statements. */ - public function getNotExpression($expression) + public function getCreateTableSQL(Table $table): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.', - ); + return $this->buildCreateTableSQL($table, true); + } - return 'NOT(' . $expression . ')'; + public function createSelectSQLBuilder(): SelectSQLBuilder + { + return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', 'SKIP LOCKED'); } /** - * Returns the SQL that checks if an expression is null. - * - * @deprecated Use IS NULL in SQL instead. - * - * @param string $expression The expression that should be compared to null. + * @internal * - * @return string The logical expression. + * @return list */ - public function getIsNullExpression($expression) + final protected function getCreateTableWithoutForeignKeysSQL(Table $table): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.', - ); - - return $expression . ' IS NULL'; + return $this->buildCreateTableSQL($table, false); } - /** - * Returns the SQL that checks if an expression is not null. - * - * @deprecated Use IS NOT NULL in SQL instead. - * - * @param string $expression The expression that should be compared to null. - * - * @return string The logical expression. - */ - public function getIsNotNullExpression($expression) + /** @return list */ + private function buildCreateTableSQL(Table $table, bool $createForeignKeys): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.', - ); + if (count($table->getColumns()) === 0) { + throw NoColumnsSpecifiedForTable::new($table->getName()); + } + + $tableName = $table->getQuotedName($this); + $options = $table->getOptions(); + $options['uniqueConstraints'] = []; + $options['indexes'] = []; + $options['primary'] = []; + + foreach ($table->getIndexes() as $index) { + if (! $index->isPrimary()) { + $options['indexes'][$index->getQuotedName($this)] = $index; + + continue; + } + + $options['primary'] = $index->getQuotedColumns($this); + $options['primary_index'] = $index; + } + + foreach ($table->getUniqueConstraints() as $uniqueConstraint) { + $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint; + } + + if ($createForeignKeys) { + $options['foreignKeys'] = []; + + foreach ($table->getForeignKeys() as $fkConstraint) { + $options['foreignKeys'][] = $fkConstraint; + } + } + + $columnSql = []; + $columns = []; + + foreach ($table->getColumns() as $column) { + $columnData = $this->columnToArray($column); + + if (in_array($column->getName(), $options['primary'], true)) { + $columnData['primary'] = true; + } + + $columns[] = $columnData; + } + + $sql = $this->_getCreateTableSQL($tableName, $columns, $options); + + if ($this->supportsCommentOnStatement()) { + if ($table->hasOption('comment')) { + $sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment')); + } + + foreach ($table->getColumns() as $column) { + $comment = $column->getComment(); + + if ($comment === '') { + continue; + } + + $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); + } + } - return $expression . ' IS NOT NULL'; + return array_merge($sql, $columnSql); } /** - * Returns the SQL that checks if an expression evaluates to a value between two values. - * - * The parameter $expression is checked if it is between $value1 and $value2. - * - * Note: There is a slight difference in the way BETWEEN works on some databases. - * http://www.w3schools.com/sql/sql_between.asp. If you want complete database - * independence you should avoid using between(). - * - * @deprecated Use BETWEEN in SQL instead. - * - * @param string $expression The value to compare to. - * @param string $value1 The lower value to compare with. - * @param string $value2 The higher value to compare with. + * @param array $tables * - * @return string The logical expression. + * @return list */ - public function getBetweenExpression($expression, $value1, $value2) + public function getCreateTablesSQL(array $tables): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.', - ); + $sql = []; + + foreach ($tables as $table) { + $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); + } - return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2; + foreach ($tables as $table) { + foreach ($table->getForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL( + $foreignKey, + $table->getQuotedName($this), + ); + } + } + + return $sql; } /** - * Returns the SQL to get the arccosine of a value. - * - * @deprecated Use ACOS() in SQL instead. - * - * @param string $value + * @param array
$tables * - * @return string + * @return list */ - public function getAcosExpression($value) + public function getDropTablesSQL(array $tables): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.', - ); + $sql = []; - return 'ACOS(' . $value . ')'; + foreach ($tables as $table) { + foreach ($table->getForeignKeys() as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL( + $foreignKey->getQuotedName($this), + $table->getQuotedName($this), + ); + } + } + + foreach ($tables as $table) { + $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); + } + + return $sql; } - /** - * Returns the SQL to get the sine of a value. - * - * @deprecated Use SIN() in SQL instead. - * - * @param string $value - * - * @return string - */ - public function getSinExpression($value) + protected function getCommentOnTableSQL(string $tableName, string $comment): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.', + $tableName = new Identifier($tableName); + + return sprintf( + 'COMMENT ON TABLE %s IS %s', + $tableName->getQuotedName($this), + $this->quoteStringLiteral($comment), ); + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getCommentOnColumnSQL(string $tableName, string $columnName, string $comment): string + { + $tableName = new Identifier($tableName); + $columnName = new Identifier($columnName); - return 'SIN(' . $value . ')'; + return sprintf( + 'COMMENT ON COLUMN %s.%s IS %s', + $tableName->getQuotedName($this), + $columnName->getQuotedName($this), + $this->quoteStringLiteral($comment), + ); } /** - * Returns the SQL to get the PI value. - * - * @deprecated Use PI() in SQL instead. + * Returns the SQL to create inline comment on a column. * - * @return string + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getPiExpression() + public function getInlineColumnCommentSQL(string $comment): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.', - ); + if (! $this->supportsInlineColumnComments()) { + throw NotSupported::new(__METHOD__); + } - return 'PI()'; + return 'COMMENT ' . $this->quoteStringLiteral($comment); } /** - * Returns the SQL to get the cosine of a value. - * - * @deprecated Use COS() in SQL instead. + * Returns the SQL used to create a table. * - * @param string $value + * @param mixed[][] $columns + * @param mixed[] $options * - * @return string + * @return array */ - public function getCosExpression($value) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.', - ); + $columnListSql = $this->getColumnDeclarationListSQL($columns); - return 'COS(' . $value . ')'; - } + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition); + } + } - /** - * Returns the SQL to calculate the difference in days between the two passed dates. - * - * Computes diff = date1 - date2. - * - * @param string $date1 - * @param string $date2 - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDateDiffExpression($date1, $date2) - { - throw Exception::notSupported(__METHOD__); - } + if (isset($options['primary']) && ! empty($options['primary'])) { + $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; + } - /** - * Returns the SQL to add the number of given seconds to a date. - * - * @param string $date - * @param int|string $seconds - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDateAddSecondsExpression($date, $seconds) - { - if (is_int($seconds)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $index => $definition) { + $columnListSql .= ', ' . $this->getIndexDeclarationSQL($definition); + } } - return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND); - } + $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; + $check = $this->getCheckDeclarationSQL($columns); - /** - * Returns the SQL to subtract the number of given seconds from a date. - * - * @param string $date - * @param int|string $seconds - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDateSubSecondsExpression($date, $seconds) - { - if (is_int($seconds)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (! empty($check)) { + $query .= ', ' . $check; } - return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND); - } + $query .= ')'; - /** - * Returns the SQL to add the number of given minutes to a date. - * - * @param string $date - * @param int|string $minutes - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDateAddMinutesExpression($date, $minutes) - { - if (is_int($minutes)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', - ); + $sql = [$query]; + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $definition) { + $sql[] = $this->getCreateForeignKeySQL($definition, $name); + } } - return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE); + return $sql; } - /** - * Returns the SQL to subtract the number of given minutes from a date. - * - * @param string $date - * @param int|string $minutes - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDateSubMinutesExpression($date, $minutes) + public function getCreateTemporaryTableSnippetSQL(): string { - if (is_int($minutes)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE); + return 'CREATE TEMPORARY TABLE'; } /** - * Returns the SQL to add the number of given hours to a date. - * - * @param string $date - * @param int|string $hours - * - * @return string + * Generates SQL statements that can be used to apply the diff. * - * @throws Exception If not supported on this platform. + * @return list */ - public function getDateAddHourExpression($date, $hours) + public function getAlterSchemaSQL(SchemaDiff $diff): array { - if (is_int($hours)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', - ); + $sql = []; + + if ($this->supportsSchemas()) { + foreach ($diff->getCreatedSchemas() as $schema) { + $sql[] = $this->getCreateSchemaSQL($schema); + } } - return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR); + if ($this->supportsSequences()) { + foreach ($diff->getAlteredSequences() as $sequence) { + $sql[] = $this->getAlterSequenceSQL($sequence); + } + + foreach ($diff->getDroppedSequences() as $sequence) { + $sql[] = $this->getDropSequenceSQL($sequence->getQuotedName($this)); + } + + foreach ($diff->getCreatedSequences() as $sequence) { + $sql[] = $this->getCreateSequenceSQL($sequence); + } + } + + $sql = array_merge( + $sql, + $this->getCreateTablesSQL( + $diff->getCreatedTables(), + ), + $this->getDropTablesSQL( + $diff->getDroppedTables(), + ), + ); + + foreach ($diff->getAlteredTables() as $tableDiff) { + $sql = array_merge($sql, $this->getAlterTableSQL($tableDiff)); + } + + return $sql; } /** - * Returns the SQL to subtract the number of given hours to a date. - * - * @param string $date - * @param int|string $hours - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to create a sequence on this platform. */ - public function getDateSubHourExpression($date, $hours) + public function getCreateSequenceSQL(Sequence $sequence): string { - if (is_int($hours)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR); + throw NotSupported::new(__METHOD__); } /** - * Returns the SQL to add the number of given days to a date. - * - * @param string $date - * @param int|string $days - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to change a sequence on this platform. */ - public function getDateAddDaysExpression($date, $days) + public function getAlterSequenceSQL(Sequence $sequence): string { - if (is_int($days)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY); + throw NotSupported::new(__METHOD__); } /** - * Returns the SQL to subtract the number of given days to a date. - * - * @param string $date - * @param int|string $days - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL snippet to drop an existing sequence. */ - public function getDateSubDaysExpression($date, $days) + public function getDropSequenceSQL(string $name): string { - if (is_int($days)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (! $this->supportsSequences()) { + throw NotSupported::new(__METHOD__); } - return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY); + return 'DROP SEQUENCE ' . $name; } /** - * Returns the SQL to add the number of given weeks to a date. - * - * @param string $date - * @param int|string $weeks - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to create an index on a table on this platform. */ - public function getDateAddWeeksExpression($date, $weeks) + public function getCreateIndexSQL(Index $index, string $table): string { - if (is_int($weeks)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', - ); + $name = $index->getQuotedName($this); + $columns = $index->getColumns(); + + if (count($columns) === 0) { + throw new InvalidArgumentException(sprintf( + 'Incomplete or invalid index definition %s on table %s', + $name, + $table, + )); } - return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK); + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index); + + return $query; } /** - * Returns the SQL to subtract the number of given weeks from a date. - * - * @param string $date - * @param int|string $weeks - * - * @return string - * - * @throws Exception If not supported on this platform. + * Adds condition for partial index. */ - public function getDateSubWeeksExpression($date, $weeks) + protected function getPartialIndexSQL(Index $index): string { - if (is_int($weeks)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', - ); + if ($this->supportsPartialIndexes() && $index->hasOption('where')) { + return ' WHERE ' . $index->getOption('where'); } - return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK); + return ''; } /** - * Returns the SQL to add the number of given months to a date. - * - * @param string $date - * @param int|string $months - * - * @return string - * - * @throws Exception If not supported on this platform. + * Adds additional flags for index generation. */ - public function getDateAddMonthExpression($date, $months) + protected function getCreateIndexSQLFlags(Index $index): string { - if (is_int($months)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH); + return $index->isUnique() ? 'UNIQUE ' : ''; } /** - * Returns the SQL to subtract the number of given months to a date. - * - * @param string $date - * @param int|string $months - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to create an unnamed primary key constraint. */ - public function getDateSubMonthExpression($date, $months) + public function getCreatePrimaryKeySQL(Index $index, string $table): string { - if (is_int($months)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH); + return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . implode(', ', $index->getQuotedColumns($this)) . ')'; } /** - * Returns the SQL to add the number of given quarters to a date. - * - * @param string $date - * @param int|string $quarters - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to create a named schema. */ - public function getDateAddQuartersExpression($date, $quarters) + public function getCreateSchemaSQL(string $schemaName): string { - if (is_int($quarters)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (! $this->supportsSchemas()) { + throw NotSupported::new(__METHOD__); } - return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER); + return 'CREATE SCHEMA ' . $schemaName; } /** - * Returns the SQL to subtract the number of given quarters from a date. - * - * @param string $date - * @param int|string $quarters - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL to create a unique constraint on a table on this platform. */ - public function getDateSubQuartersExpression($date, $quarters) + public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string { - if (is_int($quarters)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', - ); - } - - return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER); + return 'ALTER TABLE ' . $tableName . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this) . ' UNIQUE' + . ' (' . implode(', ', $constraint->getQuotedColumns($this)) . ')'; } /** - * Returns the SQL to add the number of given years to a date. - * - * @param string $date - * @param int|string $years - * - * @return string - * - * @throws Exception If not supported on this platform. + * Returns the SQL snippet to drop a schema. */ - public function getDateAddYearsExpression($date, $years) + public function getDropSchemaSQL(string $schemaName): string { - if (is_int($years)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (! $this->supportsSchemas()) { + throw NotSupported::new(__METHOD__); } - return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR); + return 'DROP SCHEMA ' . $schemaName; } /** - * Returns the SQL to subtract the number of given years from a date. + * Quotes a string so that it can be safely used as a table or column name, + * even if it is a reserved word of the platform. This also detects identifier + * chains separated by dot and quotes them independently. * - * @param string $date - * @param int|string $years + * NOTE: Just because you CAN use quoted identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. * - * @return string + * @param string $identifier The identifier name to be quoted. * - * @throws Exception If not supported on this platform. + * @return string The quoted identifier string. */ - public function getDateSubYearsExpression($date, $years) + public function quoteIdentifier(string $identifier): string { - if (is_int($years)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3498', - 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', - ); + if (str_contains($identifier, '.')) { + $parts = array_map($this->quoteSingleIdentifier(...), explode('.', $identifier)); + + return implode('.', $parts); } - return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR); + return $this->quoteSingleIdentifier($identifier); } /** - * Returns the SQL for a date arithmetic expression. - * - * @param string $date The column or literal representing a date - * to perform the arithmetic operation on. - * @param string $operator The arithmetic operator (+ or -). - * @param int|string $interval The interval that shall be calculated into the date. - * @param string $unit The unit of the interval that shall be calculated into the date. - * One of the {@see DateIntervalUnit} constants. + * Quotes a single identifier (no dot chain separation). * - * @return string + * @param string $str The identifier name to be quoted. * - * @throws Exception If not supported on this platform. + * @return string The quoted identifier string. */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) + public function quoteSingleIdentifier(string $str): string { - throw Exception::notSupported(__METHOD__); + return '"' . str_replace('"', '""', $str) . '"'; } /** - * Generates the SQL expression which represents the given date interval multiplied by a number + * Returns the SQL to create a new foreign key. * - * @param string $interval SQL expression describing the interval value - * @param int $multiplier Interval multiplier + * @param ForeignKeyConstraint $foreignKey The foreign key constraint. + * @param string $table The name of the table on which the foreign key is to be created. */ - protected function multiplyInterval(string $interval, int $multiplier): string + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, string $table): string { - return sprintf('(%s * %d)', $interval, $multiplier); + return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); } /** - * Returns the SQL bit AND comparison expression. + * Gets the SQL statements for altering an existing table. * - * @param string $value1 - * @param string $value2 + * This method returns an array of SQL statements, since some platforms need several statements. * - * @return string + * @return list */ - public function getBitAndComparisonExpression($value1, $value2) + abstract public function getAlterTableSQL(TableDiff $diff): array; + + public function getRenameTableSQL(string $oldName, string $newName): string { - return '(' . $value1 . ' & ' . $value2 . ')'; + return sprintf('ALTER TABLE %s RENAME TO %s', $oldName, $newName); } - /** - * Returns the SQL bit OR comparison expression. - * - * @param string $value1 - * @param string $value2 - * - * @return string - */ - public function getBitOrComparisonExpression($value1, $value2) + /** @return list */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array { - return '(' . $value1 . ' | ' . $value2 . ')'; - } + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); - /** - * Returns the SQL expression which represents the currently selected database. - */ - abstract public function getCurrentDatabaseExpression(): string; + $sql = []; - /** - * Returns the FOR UPDATE expression. - * - * @deprecated This API is not portable. Use {@link QueryBuilder::forUpdate()}` instead. - * - * @return string - */ - public function getForUpdateSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6191', - '%s is deprecated as non-portable.', - __METHOD__, - ); + foreach ($diff->getDroppedForeignKeys() as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); + } - return 'FOR UPDATE'; - } + foreach ($diff->getModifiedForeignKeys() as $foreignKey) { + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); + } - /** - * Honors that some SQL vendors such as MsSql use table hints for locking instead of the - * ANSI SQL FOR UPDATE specification. - * - * @param string $fromClause The FROM clause to append the hint for the given lock mode to - * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants - * @psalm-param LockMode::* $lockMode - */ - public function appendLockHint(string $fromClause, int $lockMode): string - { - switch ($lockMode) { - case LockMode::NONE: - case LockMode::OPTIMISTIC: - case LockMode::PESSIMISTIC_READ: - case LockMode::PESSIMISTIC_WRITE: - return $fromClause; - - default: - throw InvalidLockMode::fromLockMode($lockMode); + foreach ($diff->getDroppedIndexes() as $index) { + $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); } - } - - /** - * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock. - * - * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database - * vendors allow to lighten this constraint up to be a real read lock. - * - * @deprecated This API is not portable. - * - * @return string - */ - public function getReadLockSQL() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6191', - '%s is deprecated as non-portable.', - __METHOD__, - ); - - return $this->getForUpdateSQL(); - } - /** - * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. - * - * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard. - * - * @deprecated This API is not portable. - * - * @return string - */ - public function getWriteLockSQL() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6191', - '%s is deprecated as non-portable.', - __METHOD__, - ); + foreach ($diff->getModifiedIndexes() as $index) { + $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); + } - return $this->getForUpdateSQL(); + return $sql; } - /** - * Returns the SQL snippet to drop an existing table. - * - * @param Table|string $table - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getDropTableSQL($table) + /** @return list */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff): array { - $tableArg = $table; + $sql = []; - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); - $table = $table->getQuotedName($this); + foreach ($diff->getAddedForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } - if (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', - ); + foreach ($diff->getModifiedForeignKeys() as $foreignKey) { + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } - if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaDropTable, - ); - - $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); - - if ($eventArgs->isDefaultPrevented()) { - $sql = $eventArgs->getSql(); - - if ($sql === null) { - throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL'); - } - - return $sql; - } + foreach ($diff->getAddedIndexes() as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } - return 'DROP TABLE ' . $table; - } - - /** - * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. - * - * @param Table|string $table - * - * @return string - */ - public function getDropTemporaryTableSQL($table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); + foreach ($diff->getModifiedIndexes() as $index) { + $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } - return $this->getDropTableSQL($table); - } - - /** - * Returns the SQL to drop an index from a table. - * - * @param Index|string $index - * @param Table|string|null $table - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $index = $index->getQuotedName($this); - } elseif (! is_string($index)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', + foreach ($diff->getRenamedIndexes() as $oldIndexName => $index) { + $oldIndexName = new Identifier($oldIndexName); + $sql = array_merge( + $sql, + $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableNameSQL), ); } - return 'DROP INDEX ' . $index; + return $sql; } /** - * Returns the SQL to drop a constraint. - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. + * Returns the SQL for renaming an index on a table. * - * @param Constraint|string $constraint - * @param Table|string $table + * @param string $oldIndexName The name of the index to rename from. + * @param Index $index The definition of the index to rename to. + * @param string $tableName The table to rename the given index on. * - * @return string + * @return list The sequence of SQL statements for renaming the given index. */ - public function getDropConstraintSQL($constraint, $table) + protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array { - if ($constraint instanceof Constraint) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $constraint as a Constraint object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $constraint = new Identifier($constraint); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $table = new Identifier($table); - } - - $constraint = $constraint->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; + return [ + $this->getDropIndexSQL($oldIndexName, $tableName), + $this->getCreateIndexSQL($index, $tableName), + ]; } /** - * Returns the SQL to drop a foreign key. + * Gets declaration of a number of columns in bulk. * - * @param ForeignKeyConstraint|string $foreignKey - * @param Table|string $table + * @param mixed[][] $columns A multidimensional array. + * The first dimension determines the ordinal position of the column, + * while the second dimension is keyed with the name of the properties + * of the column being declared as array indexes. Currently, the types + * of supported column properties are as follows: * - * @return string + * length + * Integer value that determines the maximum length of the text + * column. If this argument is missing the column should be + * declared to have the longest length allowed by the DBMS. + * default + * Text value to be used as default for this column. + * notnull + * Boolean flag that indicates whether this column is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this column. + * collation + * Text value with the default COLLATION for this column. */ - public function getDropForeignKeySQL($foreignKey, $table) + public function getColumnDeclarationListSQL(array $columns): string { - if ($foreignKey instanceof ForeignKeyConstraint) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' - . ' Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $foreignKey = new Identifier($foreignKey); - } + $declarations = []; - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $table = new Identifier($table); + foreach ($columns as $column) { + $declarations[] = $this->getColumnDeclarationSQL($column['name'], $column); } - $foreignKey = $foreignKey->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; - } - - /** - * Returns the SQL to drop a unique constraint. - */ - public function getDropUniqueConstraintSQL(string $name, string $tableName): string - { - return $this->getDropConstraintSQL($name, $tableName); + return implode(', ', $declarations); } /** - * Returns the SQL statement(s) to create a table with the specified name, columns and constraints - * on this platform. - * - * @param int $createFlags - * @psalm-param int-mask-of $createFlags - * - * @return list The list of SQL statements. + * Obtains DBMS specific SQL code portion needed to declare a generic type + * column to be used in statements like CREATE TABLE. * - * @throws Exception - * @throws InvalidArgumentException - */ - public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) - { - if (! is_int($createFlags)) { - throw new InvalidArgumentException( - 'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.', - ); - } - - if (($createFlags & self::CREATE_INDEXES) === 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5416', - 'Unsetting the CREATE_INDEXES flag in AbstractPlatform::getCreateTableSQL() is deprecated.', - ); - } - - if (($createFlags & self::CREATE_FOREIGNKEYS) === 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5416', - 'Not setting the CREATE_FOREIGNKEYS flag in AbstractPlatform::getCreateTableSQL()' - . ' is deprecated. In order to build the statements that create multiple tables' - . ' referencing each other via foreign keys, use AbstractPlatform::getCreateTablesSQL().', - ); - } - - return $this->buildCreateTableSQL( - $table, - ($createFlags & self::CREATE_INDEXES) > 0, - ($createFlags & self::CREATE_FOREIGNKEYS) > 0, - ); - } - - public function createSelectSQLBuilder(): SelectSQLBuilder - { - return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', 'SKIP LOCKED'); - } - - /** - * @internal + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @return list + * @param string $name The name the column to be declared. + * @param mixed[] $column An associative array with the name of the properties + * of the column being declared as array indexes. Currently, the types + * of supported column properties are as follows: * - * @throws Exception - */ - final protected function getCreateTableWithoutForeignKeysSQL(Table $table): array - { - return $this->buildCreateTableSQL($table, true, false); - } - - /** - * @return list + * length + * Integer value that determines the maximum length of the text + * column. If this argument is missing the column should be + * declared to have the longest length allowed by the DBMS. + * default + * Text value to be used as default for this column. + * notnull + * Boolean flag that indicates whether this column is constrained + * to not be set to null. + * charset + * Text value with the default CHARACTER SET for this column. + * collation + * Text value with the default COLLATION for this column. + * columnDefinition + * a string that defines the complete column * - * @throws Exception + * @return string DBMS specific SQL code portion that should be used to declare the column. */ - private function buildCreateTableSQL(Table $table, bool $createIndexes, bool $createForeignKeys): array + public function getColumnDeclarationSQL(string $name, array $column): string { - if (count($table->getColumns()) === 0) { - throw Exception::noColumnsSpecifiedForTable($table->getName()); - } - - $tableName = $table->getQuotedName($this); - $options = $table->getOptions(); - $options['uniqueConstraints'] = []; - $options['indexes'] = []; - $options['primary'] = []; - - if ($createIndexes) { - foreach ($table->getIndexes() as $index) { - if (! $index->isPrimary()) { - $options['indexes'][$index->getQuotedName($this)] = $index; - - continue; - } - - $options['primary'] = $index->getQuotedColumns($this); - $options['primary_index'] = $index; - } - - foreach ($table->getUniqueConstraints() as $uniqueConstraint) { - $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint; - } - } - - if ($createForeignKeys) { - $options['foreignKeys'] = []; - - foreach ($table->getForeignKeys() as $fkConstraint) { - $options['foreignKeys'][] = $fkConstraint; - } - } - - $columnSql = []; - $columns = []; - - foreach ($table->getColumns() as $column) { - if ( - $this->_eventManager !== null - && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn) - ) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaCreateTableColumn, - ); - - $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); - - $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); - - $columnSql = array_merge($columnSql, $eventArgs->getSql()); - - if ($eventArgs->isDefaultPrevented()) { - continue; - } - } - - $columnData = $this->columnToArray($column); - - if (in_array($column->getName(), $options['primary'], true)) { - $columnData['primary'] = true; - } + if (isset($column['columnDefinition'])) { + $declaration = $column['columnDefinition']; + } else { + $default = $this->getDefaultValueDeclarationSQL($column); - $columns[$columnData['name']] = $columnData; - } + $charset = ! empty($column['charset']) ? + ' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : ''; - if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaCreateTable, - ); + $collation = ! empty($column['collation']) ? + ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; - $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); + $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); + $typeDecl = $column['type']->getSQLDeclaration($column, $this); + $declaration = $typeDecl . $charset . $default . $notnull . $collation; - if ($eventArgs->isDefaultPrevented()) { - return array_merge($eventArgs->getSql(), $columnSql); + if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') { + $declaration .= ' ' . $this->getInlineColumnCommentSQL($column['comment']); } } - $sql = $this->_getCreateTableSQL($tableName, $columns, $options); - - if ($this->supportsCommentOnStatement()) { - if ($table->hasOption('comment')) { - $sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment')); - } - - foreach ($table->getColumns() as $column) { - $comment = $this->getColumnComment($column); - - if ($comment === null || $comment === '') { - continue; - } - - $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); - } - } - - return array_merge($sql, $columnSql); - } - - /** - * @param list
$tables - * - * @return list - * - * @throws Exception - */ - public function getCreateTablesSQL(array $tables): array - { - $sql = []; - - foreach ($tables as $table) { - $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); - } - - foreach ($tables as $table) { - foreach ($table->getForeignKeys() as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL( - $foreignKey, - $table->getQuotedName($this), - ); - } - } - - return $sql; - } - - /** - * @param list
$tables - * - * @return list - */ - public function getDropTablesSQL(array $tables): array - { - $sql = []; - - foreach ($tables as $table) { - foreach ($table->getForeignKeys() as $foreignKey) { - $sql[] = $this->getDropForeignKeySQL( - $foreignKey->getQuotedName($this), - $table->getQuotedName($this), - ); - } - } - - foreach ($tables as $table) { - $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); - } - - return $sql; - } - - protected function getCommentOnTableSQL(string $tableName, ?string $comment): string - { - $tableName = new Identifier($tableName); - - return sprintf( - 'COMMENT ON TABLE %s IS %s', - $tableName->getQuotedName($this), - $this->quoteStringLiteral((string) $comment), - ); - } - - /** - * @param string $tableName - * @param string $columnName - * @param string|null $comment - * - * @return string - */ - public function getCommentOnColumnSQL($tableName, $columnName, $comment) - { - $tableName = new Identifier($tableName); - $columnName = new Identifier($columnName); - - return sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName->getQuotedName($this), - $columnName->getQuotedName($this), - $this->quoteStringLiteral((string) $comment), - ); - } - - /** - * Returns the SQL to create inline comment on a column. - * - * @param string $comment - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getInlineColumnCommentSQL($comment) - { - if (! $this->supportsInlineColumnComments()) { - throw Exception::notSupported(__METHOD__); - } - - return 'COMMENT ' . $this->quoteStringLiteral($comment); - } - - /** - * Returns the SQL used to create a table. - * - * @param string $name - * @param mixed[][] $columns - * @param mixed[] $options - * - * @return string[] - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $columnListSql = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $index => $definition) { - $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); - } - } - - if (isset($options['primary']) && ! empty($options['primary'])) { - $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; - } - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $index => $definition) { - $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); - } - } - - $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; - $check = $this->getCheckDeclarationSQL($columns); - - if (! empty($check)) { - $query .= ', ' . $check; - } - - $query .= ')'; - - $sql = [$query]; - - if (isset($options['foreignKeys'])) { - foreach ($options['foreignKeys'] as $definition) { - $sql[] = $this->getCreateForeignKeySQL($definition, $name); - } - } - - return $sql; - } - - /** @return string */ - public function getCreateTemporaryTableSnippetSQL() - { - return 'CREATE TEMPORARY TABLE'; - } - - /** - * Generates SQL statements that can be used to apply the diff. - * - * @return list - */ - public function getAlterSchemaSQL(SchemaDiff $diff): array - { - return $diff->toSql($this); - } - - /** - * Returns the SQL to create a sequence on this platform. - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getCreateSequenceSQL(Sequence $sequence) - { - throw Exception::notSupported(__METHOD__); - } - - /** - * Returns the SQL to change a sequence on this platform. - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getAlterSequenceSQL(Sequence $sequence) - { - throw Exception::notSupported(__METHOD__); - } - - /** - * Returns the SQL snippet to drop an existing sequence. - * - * @param Sequence|string $sequence - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getDropSequenceSQL($sequence) - { - if (! $this->supportsSequences()) { - throw Exception::notSupported(__METHOD__); - } - - if ($sequence instanceof Sequence) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $sequence as a Sequence object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $sequence = $sequence->getQuotedName($this); - } - - return 'DROP SEQUENCE ' . $sequence; - } - - /** - * Returns the SQL to create a constraint on a table on this platform. - * - * @deprecated Use {@see getCreateIndexSQL()}, {@see getCreateForeignKeySQL()} - * or {@see getCreateUniqueConstraintSQL()} instead. - * - * @param Table|string $table - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getCreateConstraintSQL(Constraint $constraint, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - - $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); - - $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')'; - - $referencesClause = ''; - if ($constraint instanceof Index) { - if ($constraint->isPrimary()) { - $query .= ' PRIMARY KEY'; - } elseif ($constraint->isUnique()) { - $query .= ' UNIQUE'; - } else { - throw new InvalidArgumentException( - 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().', - ); - } - } elseif ($constraint instanceof UniqueConstraint) { - $query .= ' UNIQUE'; - } elseif ($constraint instanceof ForeignKeyConstraint) { - $query .= ' FOREIGN KEY'; - - $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . - ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')'; - } - - $query .= ' ' . $columnList . $referencesClause; - - return $query; - } - - /** - * Returns the SQL to create an index on a table on this platform. - * - * @param Table|string $table The name of the table on which the index is to be created. - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getCreateIndexSQL(Index $index, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - - $name = $index->getQuotedName($this); - $columns = $index->getColumns(); - - if (count($columns) === 0) { - throw new InvalidArgumentException(sprintf( - 'Incomplete or invalid index definition %s on table %s', - $name, - $table, - )); - } - - if ($index->isPrimary()) { - return $this->getCreatePrimaryKeySQL($index, $table); - } - - $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); - - return $query; - } - - /** - * Adds condition for partial index. - * - * @return string - */ - protected function getPartialIndexSQL(Index $index) - { - if ($this->supportsPartialIndexes() && $index->hasOption('where')) { - return ' WHERE ' . $index->getOption('where'); - } - - return ''; - } - - /** - * Adds additional flags for index generation. - * - * @return string - */ - protected function getCreateIndexSQLFlags(Index $index) - { - return $index->isUnique() ? 'UNIQUE ' : ''; - } - - /** - * Returns the SQL to create an unnamed primary key constraint. - * - * @param Table|string $table - * - * @return string - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - - return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; - } - - /** - * Returns the SQL to create a named schema. - * - * @param string $schemaName - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getCreateSchemaSQL($schemaName) - { - if (! $this->supportsSchemas()) { - throw Exception::notSupported(__METHOD__); - } - - return 'CREATE SCHEMA ' . $schemaName; - } - - /** - * Returns the SQL to create a unique constraint on a table on this platform. - */ - public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string - { - return $this->getCreateConstraintSQL($constraint, $tableName); - } - - /** - * Returns the SQL snippet to drop a schema. - * - * @throws Exception If not supported on this platform. - */ - public function getDropSchemaSQL(string $schemaName): string - { - if (! $this->supportsSchemas()) { - throw Exception::notSupported(__METHOD__); - } - - return 'DROP SCHEMA ' . $schemaName; - } - - /** - * Quotes a string so that it can be safely used as a table or column name, - * even if it is a reserved word of the platform. This also detects identifier - * chains separated by dot and quotes them independently. - * - * NOTE: Just because you CAN use quoted identifiers doesn't mean - * you SHOULD use them. In general, they end up causing way more - * problems than they solve. - * - * @param string $str The identifier name to be quoted. - * - * @return string The quoted identifier string. - */ - public function quoteIdentifier($str) - { - if (strpos($str, '.') !== false) { - $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str)); - - return implode('.', $parts); - } - - return $this->quoteSingleIdentifier($str); - } - - /** - * Quotes a single identifier (no dot chain separation). - * - * @param string $str The identifier name to be quoted. - * - * @return string The quoted identifier string. - */ - public function quoteSingleIdentifier($str) - { - $c = $this->getIdentifierQuoteCharacter(); - - return $c . str_replace($c, $c . $c, $str) . $c; - } - - /** - * Returns the SQL to create a new foreign key. - * - * @param ForeignKeyConstraint $foreignKey The foreign key constraint. - * @param Table|string $table The name of the table on which the foreign key is to be created. - * - * @return string - */ - public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - - return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); - } - - /** - * Gets the SQL statements for altering an existing table. - * - * This method returns an array of SQL statements, since some platforms need several statements. - * - * @return list - * - * @throws Exception If not supported on this platform. - */ - public function getAlterTableSQL(TableDiff $diff) - { - throw Exception::notSupported(__METHOD__); - } - - /** @return list */ - public function getRenameTableSQL(string $oldName, string $newName): array - { - return [ - sprintf('ALTER TABLE %s RENAME TO %s', $oldName, $newName), - ]; - } - - /** - * @param mixed[] $columnSql - * - * @return bool - */ - protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) - { - if ($this->_eventManager === null) { - return false; - } - - if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { - return false; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaAlterTableAddColumn, - ); - - $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); - - $columnSql = array_merge($columnSql, $eventArgs->getSql()); - - return $eventArgs->isDefaultPrevented(); - } - - /** - * @param string[] $columnSql - * - * @return bool - */ - protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) - { - if ($this->_eventManager === null) { - return false; - } - - if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { - return false; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaAlterTableRemoveColumn, - ); - - $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); - - $columnSql = array_merge($columnSql, $eventArgs->getSql()); - - return $eventArgs->isDefaultPrevented(); - } - - /** - * @param string[] $columnSql - * - * @return bool - */ - protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) - { - if ($this->_eventManager === null) { - return false; - } - - if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { - return false; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaAlterTableChangeColumn, - ); - - $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); - - $columnSql = array_merge($columnSql, $eventArgs->getSql()); - - return $eventArgs->isDefaultPrevented(); - } - - /** - * @param string $oldColumnName - * @param string[] $columnSql - * - * @return bool - */ - protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) - { - if ($this->_eventManager === null) { - return false; - } - - if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { - return false; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaAlterTableRenameColumn, - ); - - $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); - - $columnSql = array_merge($columnSql, $eventArgs->getSql()); - - return $eventArgs->isDefaultPrevented(); - } - - /** - * @param string[] $sql - * - * @return bool - */ - protected function onSchemaAlterTable(TableDiff $diff, &$sql) - { - if ($this->_eventManager === null) { - return false; - } - - if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { - return false; - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated.', - Events::onSchemaAlterTable, - ); - - $eventArgs = new SchemaAlterTableEventArgs($diff, $this); - $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); - - $sql = array_merge($sql, $eventArgs->getSql()); - - return $eventArgs->isDefaultPrevented(); - } - - /** @return string[] */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) - { - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - - $sql = []; - if ($this->supportsForeignKeyConstraints()) { - foreach ($diff->getDroppedForeignKeys() as $foreignKey) { - if ($foreignKey instanceof ForeignKeyConstraint) { - $foreignKey = $foreignKey->getQuotedName($this); - } - - $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableNameSQL); - } - - foreach ($diff->getModifiedForeignKeys() as $foreignKey) { - $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); - } - } - - foreach ($diff->getDroppedIndexes() as $index) { - $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); - } - - foreach ($diff->getModifiedIndexes() as $index) { - $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); - } - - return $sql; - } - - /** @return string[] */ - protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) - { - $sql = []; - $newName = $diff->getNewName(); - - if ($newName !== false) { - $tableNameSQL = $newName->getQuotedName($this); - } else { - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); - } - - if ($this->supportsForeignKeyConstraints()) { - foreach ($diff->getAddedForeignKeys() as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); - } - - foreach ($diff->getModifiedForeignKeys() as $foreignKey) { - $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); - } - } - - foreach ($diff->getAddedIndexes() as $index) { - $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); - } - - foreach ($diff->getModifiedIndexes() as $index) { - $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); - } - - foreach ($diff->getRenamedIndexes() as $oldIndexName => $index) { - $oldIndexName = new Identifier($oldIndexName); - $sql = array_merge( - $sql, - $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableNameSQL), - ); - } - - return $sql; - } - - /** - * Returns the SQL for renaming an index on a table. - * - * @param string $oldIndexName The name of the index to rename from. - * @param Index $index The definition of the index to rename to. - * @param string $tableName The table to rename the given index on. - * - * @return string[] The sequence of SQL statements for renaming the given index. - */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) - { - return [ - $this->getDropIndexSQL($oldIndexName, $tableName), - $this->getCreateIndexSQL($index, $tableName), - ]; - } - - /** - * Gets declaration of a number of columns in bulk. - * - * @param mixed[][] $columns A multidimensional associative array. - * The first dimension determines the column name, while the second - * dimension is keyed with the name of the properties - * of the column being declared as array indexes. Currently, the types - * of supported column properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * column. If this argument is missing the column should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this column. - * - * notnull - * Boolean flag that indicates whether this column is constrained - * to not be set to null. - * charset - * Text value with the default CHARACTER SET for this column. - * collation - * Text value with the default COLLATION for this column. - * unique - * unique constraint - * - * @return string - */ - public function getColumnDeclarationListSQL(array $columns) - { - $declarations = []; - - foreach ($columns as $name => $column) { - $declarations[] = $this->getColumnDeclarationSQL($name, $column); - } - - return implode(', ', $declarations); - } - - /** - * Obtains DBMS specific SQL code portion needed to declare a generic type - * column to be used in statements like CREATE TABLE. - * - * @param string $name The name the column to be declared. - * @param mixed[] $column An associative array with the name of the properties - * of the column being declared as array indexes. Currently, the types - * of supported column properties are as follows: - * - * length - * Integer value that determines the maximum length of the text - * column. If this argument is missing the column should be - * declared to have the longest length allowed by the DBMS. - * - * default - * Text value to be used as default for this column. - * - * notnull - * Boolean flag that indicates whether this column is constrained - * to not be set to null. - * charset - * Text value with the default CHARACTER SET for this column. - * collation - * Text value with the default COLLATION for this column. - * unique - * unique constraint - * check - * column check constraint - * columnDefinition - * a string that defines the complete column - * - * @return string DBMS specific SQL code portion that should be used to declare the column. - * - * @throws Exception - */ - public function getColumnDeclarationSQL($name, array $column) - { - if (isset($column['columnDefinition'])) { - $declaration = $this->getCustomTypeDeclarationSQL($column); - } else { - $default = $this->getDefaultValueDeclarationSQL($column); - - $charset = ! empty($column['charset']) ? - ' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : ''; - - $collation = ! empty($column['collation']) ? - ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; - - $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - - if (! empty($column['unique'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', - ); - - $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); - } else { - $unique = ''; - } - - if (! empty($column['check'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "check" column property is deprecated.', - ); - - $check = ' ' . $column['check']; - } else { - $check = ''; - } - - $typeDecl = $column['type']->getSQLDeclaration($column, $this); - $declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; - - if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') { - $declaration .= ' ' . $this->getInlineColumnCommentSQL($column['comment']); - } - } - - return $name . ' ' . $declaration; - } - - /** - * Returns the SQL snippet that declares a floating point column of arbitrary precision. - * - * @param mixed[] $column - * - * @return string - */ - public function getDecimalTypeDeclarationSQL(array $column) - { - if (empty($column['precision'])) { - if (! isset($column['precision'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5637', - 'Relying on the default decimal column precision is deprecated' - . ', specify the precision explicitly.', - ); - } - - $precision = 10; - } else { - $precision = $column['precision']; - } - - if (empty($column['scale'])) { - if (! isset($column['scale'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5637', - 'Relying on the default decimal column scale is deprecated' - . ', specify the scale explicitly.', - ); - } - - $scale = 0; - } else { - $scale = $column['scale']; - } - - return 'NUMERIC(' . $precision . ', ' . $scale . ')'; - } - - /** - * Obtains DBMS specific SQL code portion needed to set a default value - * declaration to be used in statements like CREATE TABLE. - * - * @param mixed[] $column The column definition array. - * - * @return string DBMS specific SQL code portion needed to set a default value. - */ - public function getDefaultValueDeclarationSQL($column) - { - if (! isset($column['default'])) { - return empty($column['notnull']) ? ' DEFAULT NULL' : ''; - } - - $default = $column['default']; - - if (! isset($column['type'])) { - return " DEFAULT '" . $default . "'"; - } - - $type = $column['type']; - - if ($type instanceof Types\PhpIntegerMappingType) { - return ' DEFAULT ' . $default; - } - - if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) { - return ' DEFAULT ' . $this->getCurrentTimestampSQL(); - } - - if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) { - return ' DEFAULT ' . $this->getCurrentTimeSQL(); - } - - if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) { - return ' DEFAULT ' . $this->getCurrentDateSQL(); - } - - if ($type instanceof Types\BooleanType) { - return ' DEFAULT ' . $this->convertBooleans($default); - } - - return ' DEFAULT ' . $this->quoteStringLiteral($default); - } - - /** - * Obtains DBMS specific SQL code portion needed to set a CHECK constraint - * declaration to be used in statements like CREATE TABLE. - * - * @param string[]|mixed[][] $definition The check definition. - * - * @return string DBMS specific SQL code portion needed to set a CHECK constraint. - */ - public function getCheckDeclarationSQL(array $definition) - { - $constraints = []; - foreach ($definition as $column => $def) { - if (is_string($def)) { - $constraints[] = 'CHECK (' . $def . ')'; - } else { - if (isset($def['min'])) { - $constraints[] = 'CHECK (' . $column . ' >= ' . $def['min'] . ')'; - } - - if (isset($def['max'])) { - $constraints[] = 'CHECK (' . $column . ' <= ' . $def['max'] . ')'; - } - } - } - - return implode(', ', $constraints); - } - - /** - * 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 UniqueConstraint $constraint The unique constraint definition. - * - * @return string DBMS specific SQL code portion needed to set a constraint. - * - * @throws InvalidArgumentException - */ - public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint) - { - $columns = $constraint->getQuotedColumns($this); - $name = new Identifier($name); - - if (count($columns) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'columns' required."); - } - - $constraintFlags = array_merge(['UNIQUE'], array_map('strtoupper', $constraint->getFlags())); - $constraintName = $name->getQuotedName($this); - $columnListNames = $this->getColumnsFieldDeclarationListSQL($columns); - - return sprintf('CONSTRAINT %s %s (%s)', $constraintName, implode(' ', $constraintFlags), $columnListNames); - } - - /** - * Obtains DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @param string $name The name of the index. - * @param Index $index The index definition. - * - * @return string DBMS specific SQL code portion needed to set an index. - * - * @throws InvalidArgumentException - */ - public function getIndexDeclarationSQL($name, Index $index) - { - $columns = $index->getColumns(); - $name = new Identifier($name); - - if (count($columns) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'columns' required."); - } - - return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) - . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); - } - - /** - * Obtains SQL code portion needed to create a custom column, - * e.g. when a column has the "columnDefinition" keyword. - * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. - * - * @deprecated - * - * @param mixed[] $column - * - * @return string - */ - public function getCustomTypeDeclarationSQL(array $column) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5527', - '%s is deprecated.', - __METHOD__, - ); - - return $column['columnDefinition']; - } - - /** - * Obtains DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @deprecated - */ - public function getIndexFieldDeclarationListSQL(Index $index): string - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5527', - '%s is deprecated.', - __METHOD__, - ); - - return implode(', ', $index->getQuotedColumns($this)); - } - - /** - * Obtains DBMS specific SQL code portion needed to set an index - * declaration to be used in statements like CREATE TABLE. - * - * @deprecated - * - * @param mixed[] $columns - */ - public function getColumnsFieldDeclarationListSQL(array $columns): string - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5527', - '%s is deprecated.', - __METHOD__, - ); - - $ret = []; - - foreach ($columns as $column => $definition) { - if (is_array($definition)) { - $ret[] = $column; - } else { - $ret[] = $definition; - } - } - - return implode(', ', $ret); - } - - /** - * Returns the required SQL string that fits between CREATE ... TABLE - * to create the table as a temporary table. - * - * Should be overridden in driver classes to return the correct string for the - * specific database type. - * - * The default is to return the string "TEMPORARY" - this will result in a - * SQL error for any database that does not support temporary tables, or that - * requires a different SQL command from "CREATE TEMPORARY TABLE". - * - * @deprecated - * - * @return string The string required to be placed between "CREATE" and "TABLE" - * to generate a temporary table, if possible. - */ - public function getTemporaryTableSQL() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getTemporaryTableSQL() is deprecated.', - ); - - return 'TEMPORARY'; - } - - /** - * Some vendors require temporary table names to be qualified specially. - * - * @param string $tableName - * - * @return string - */ - public function getTemporaryTableName($tableName) - { - return $tableName; - } - - /** - * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a column declaration to be used in statements like CREATE TABLE. - * - * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a column declaration. - */ - public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) - { - $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); - $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); - - return $sql; - } - - /** - * Returns the FOREIGN KEY query section dealing with non-standard options - * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... - * - * @param ForeignKeyConstraint $foreignKey The foreign key definition. - * - * @return string - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = ''; - if ($foreignKey->hasOption('onUpdate')) { - $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); - } - - if ($foreignKey->hasOption('onDelete')) { - $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); - } - - return $query; - } - - /** - * Returns the given referential action in uppercase if valid, otherwise throws an exception. - * - * @param string $action The foreign key referential action. - * - * @return string - * - * @throws InvalidArgumentException If unknown referential action given. - */ - public function getForeignKeyReferentialActionSQL($action) - { - $upper = strtoupper($action); - switch ($upper) { - case 'CASCADE': - case 'SET NULL': - case 'NO ACTION': - case 'RESTRICT': - case 'SET DEFAULT': - return $upper; - - default: - throw new InvalidArgumentException('Invalid foreign key action: ' . $upper); - } - } - - /** - * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint - * of a column declaration to be used in statements like CREATE TABLE. - * - * @return string - * - * @throws InvalidArgumentException - */ - public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) - { - $sql = ''; - if (strlen($foreignKey->getName()) > 0) { - $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; - } - - $sql .= 'FOREIGN KEY ('; - - if (count($foreignKey->getLocalColumns()) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'local' required."); - } - - if (count($foreignKey->getForeignColumns()) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'foreign' required."); - } - - if (strlen($foreignKey->getForeignTableName()) === 0) { - throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required."); - } - - return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this)) - . ') REFERENCES ' - . $foreignKey->getQuotedForeignTableName($this) . ' (' - . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; - } - - /** - * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint - * of a column declaration to be used in statements like CREATE TABLE. - * - * @deprecated Use UNIQUE in SQL instead. - * - * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint - * of a column declaration. - */ - public function getUniqueFieldDeclarationSQL() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.', - ); - - return 'UNIQUE'; - } - - /** - * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET - * of a column declaration to be used in statements like CREATE TABLE. - * - * @param string $charset The name of the charset. - * - * @return string DBMS specific SQL code portion needed to set the CHARACTER SET - * of a column declaration. - */ - public function getColumnCharsetDeclarationSQL($charset) - { - return ''; - } - - /** - * Obtains DBMS specific SQL code portion needed to set the COLLATION - * of a column declaration to be used in statements like CREATE TABLE. - * - * @param string $collation The name of the collation. - * - * @return string DBMS specific SQL code portion needed to set the COLLATION - * of a column declaration. - */ - public function getColumnCollationDeclarationSQL($collation) - { - return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : ''; - } - - /** - * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. - * Subclasses should override this method to return TRUE if they prefer identity columns. - * - * @deprecated - * - * @return bool - */ - public function prefersIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/1519', - 'AbstractPlatform::prefersIdentityColumns() is deprecated.', - ); - - return false; - } - - /** - * Some platforms need the boolean values to be converted. - * - * The default conversion in this implementation converts to integers (false => 0, true => 1). - * - * Note: if the input is not a boolean the original input might be returned. - * - * There are two contexts when converting booleans: Literals and Prepared Statements. - * This method should handle the literal case - * - * @param mixed $item A boolean or an array of them. - * - * @return mixed A boolean database value or an array of them. - */ - public function convertBooleans($item) - { - if (is_array($item)) { - foreach ($item as $k => $value) { - if (! is_bool($value)) { - continue; - } - - $item[$k] = (int) $value; - } - } elseif (is_bool($item)) { - $item = (int) $item; - } - - return $item; - } - - /** - * Some platforms have boolean literals that needs to be correctly converted - * - * The default conversion tries to convert value into bool "(bool)$item" - * - * @param T $item - * - * @return (T is null ? null : bool) - * - * @template T - */ - public function convertFromBoolean($item) - { - return $item === null ? null : (bool) $item; - } - - /** - * This method should handle the prepared statements case. When there is no - * distinction, it's OK to use the same method. - * - * Note: if the input is not a boolean the original input might be returned. - * - * @param mixed $item A boolean or an array of them. - * - * @return mixed A boolean database value or an array of them. - */ - public function convertBooleansToDatabaseValue($item) - { - return $this->convertBooleans($item); - } - - /** - * Returns the SQL specific for the platform to get the current date. - * - * @return string - */ - public function getCurrentDateSQL() - { - return 'CURRENT_DATE'; + return $name . ' ' . $declaration; } /** - * Returns the SQL specific for the platform to get the current time. + * Returns the SQL snippet that declares a floating point column of arbitrary precision. * - * @return string + * @param mixed[] $column */ - public function getCurrentTimeSQL() + public function getDecimalTypeDeclarationSQL(array $column): string { - return 'CURRENT_TIME'; - } + if (! isset($column['precision'])) { + $e = ColumnPrecisionRequired::new(); + } elseif (! isset($column['scale'])) { + $e = ColumnScaleRequired::new(); + } else { + $e = null; + } - /** - * Returns the SQL specific for the platform to get the current timestamp - * - * @return string - */ - public function getCurrentTimestampSQL() - { - return 'CURRENT_TIMESTAMP'; + if ($e !== null) { + throw InvalidColumnDeclaration::fromInvalidColumnType($column['name'], $e); + } + + return 'NUMERIC(' . $column['precision'] . ', ' . $column['scale'] . ')'; } /** - * Returns the SQL for a given transaction isolation level Connection constant. + * Obtains DBMS specific SQL code portion needed to set a default value + * declaration to be used in statements like CREATE TABLE. * - * @param int $level + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @return string + * @param mixed[] $column The column definition array. * - * @throws InvalidArgumentException + * @return string DBMS specific SQL code portion needed to set a default value. */ - protected function _getTransactionIsolationLevelSQL($level) + public function getDefaultValueDeclarationSQL(array $column): string { - switch ($level) { - case TransactionIsolationLevel::READ_UNCOMMITTED: - return 'READ UNCOMMITTED'; + if (! isset($column['default'])) { + return empty($column['notnull']) ? ' DEFAULT NULL' : ''; + } - case TransactionIsolationLevel::READ_COMMITTED: - return 'READ COMMITTED'; + $default = $column['default']; - case TransactionIsolationLevel::REPEATABLE_READ: - return 'REPEATABLE READ'; + if (! isset($column['type'])) { + return " DEFAULT '" . $default . "'"; + } - case TransactionIsolationLevel::SERIALIZABLE: - return 'SERIALIZABLE'; + $type = $column['type']; - default: - throw new InvalidArgumentException('Invalid isolation level:' . $level); + if ($type instanceof Types\PhpIntegerMappingType) { + return ' DEFAULT ' . $default; } - } - /** - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getListDatabasesSQL() - { - throw Exception::notSupported(__METHOD__); - } + if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) { + return ' DEFAULT ' . $this->getCurrentTimestampSQL(); + } - /** - * Returns the SQL statement for retrieving the namespaces defined in the database. - * - * @deprecated Use {@see AbstractSchemaManager::listSchemaNames()} instead. - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getListNamespacesSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'AbstractPlatform::getListNamespacesSQL() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.', - ); + if ($type instanceof Types\PhpTimeMappingType && $default === $this->getCurrentTimeSQL()) { + return ' DEFAULT ' . $this->getCurrentTimeSQL(); + } + + if ($type instanceof Types\PhpDateMappingType && $default === $this->getCurrentDateSQL()) { + return ' DEFAULT ' . $this->getCurrentDateSQL(); + } + + if ($type instanceof Types\BooleanType) { + return ' DEFAULT ' . $this->convertBooleans($default); + } + + if (is_int($default) || is_float($default)) { + return ' DEFAULT ' . $default; + } - throw Exception::notSupported(__METHOD__); + return ' DEFAULT ' . $this->quoteStringLiteral($default); } /** - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - * - * @param string $database + * Obtains DBMS specific SQL code portion needed to set a CHECK constraint + * declaration to be used in statements like CREATE TABLE. * - * @return string + * @param string[]|mixed[][] $definition The check definition. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set a CHECK constraint. */ - public function getListSequencesSQL($database) + public function getCheckDeclarationSQL(array $definition): string { - throw Exception::notSupported(__METHOD__); + $constraints = []; + foreach ($definition as $def) { + if (is_string($def)) { + $constraints[] = 'CHECK (' . $def . ')'; + } else { + if (isset($def['min'])) { + $constraints[] = 'CHECK (' . $def['name'] . ' >= ' . $def['min'] . ')'; + } + + if (! isset($def['max'])) { + continue; + } + + $constraints[] = 'CHECK (' . $def['name'] . ' <= ' . $def['max'] . ')'; + } + } + + return implode(', ', $constraints); } /** - * @deprecated - * - * @param string $table + * Obtains DBMS specific SQL code portion needed to set a unique + * constraint declaration to be used in statements like CREATE TABLE. * - * @return string + * @param UniqueConstraint $constraint The unique constraint definition. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set a constraint. */ - public function getListTableConstraintsSQL($table) + public function getUniqueConstraintDeclarationSQL(UniqueConstraint $constraint): string { - throw Exception::notSupported(__METHOD__); + $columns = $constraint->getColumns(); + + if (count($columns) === 0) { + throw new InvalidArgumentException('Incomplete definition. "columns" required.'); + } + + $chunks = ['CONSTRAINT']; + + if ($constraint->getName() !== '') { + $chunks[] = $constraint->getQuotedName($this); + } + + $chunks[] = 'UNIQUE'; + + if ($constraint->hasFlag('clustered')) { + $chunks[] = 'CLUSTERED'; + } + + $chunks[] = sprintf('(%s)', implode(', ', $columns)); + + return implode(' ', $chunks); } /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. + * Obtains DBMS specific SQL code portion needed to set an index + * declaration to be used in statements like CREATE TABLE. * - * @param string $table - * @param string $database + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @return string + * @param Index $index The index definition. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set an index. */ - public function getListTableColumnsSQL($table, $database = null) + public function getIndexDeclarationSQL(Index $index): string { - throw Exception::notSupported(__METHOD__); + $columns = $index->getColumns(); + + if (count($columns) === 0) { + throw new InvalidArgumentException('Incomplete definition. "columns" required.'); + } + + return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $index->getQuotedName($this) + . ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index); } /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @return string - * - * @throws Exception If not supported on this platform. + * Some vendors require temporary table names to be qualified specially. */ - public function getListTablesSQL() + public function getTemporaryTableName(string $tableName): string { - throw Exception::notSupported(__METHOD__); + return $tableName; } /** - * @deprecated + * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a column declaration to be used in statements like CREATE TABLE. * - * @return string + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a column declaration. */ - public function getListUsersSQL() + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::getListUsersSQL() is deprecated.', - ); + $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); + $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); - throw Exception::notSupported(__METHOD__); + return $sql; } /** - * Returns the SQL to list all views of a database or user. - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - * - * @param string $database + * Returns the FOREIGN KEY query section dealing with non-standard options + * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... * - * @return string + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @throws Exception If not supported on this platform. + * @param ForeignKeyConstraint $foreignKey The foreign key definition. */ - public function getListViewsSQL($database) + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string { - throw Exception::notSupported(__METHOD__); - } + $query = ''; + if ($foreignKey->hasOption('onUpdate')) { + $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); + } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * Returns the list of indexes for the current database. - * - * The current database parameter is optional but will always be passed - * when using the SchemaManager API and is the database the given table is in. - * - * Attention: Some platforms only support currentDatabase when they - * are connected with that database. Cross-database information schema - * requests may be impossible. - * - * @param string $table - * @param string $database - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getListTableIndexesSQL($table, $database = null) - { - throw Exception::notSupported(__METHOD__); + if ($foreignKey->hasOption('onDelete')) { + $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); + } + + return $query; } /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @param string $table + * Returns the given referential action in uppercase if valid, otherwise throws an exception. * - * @return string + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @throws Exception If not supported on this platform. + * @param string $action The foreign key referential action. */ - public function getListTableForeignKeysSQL($table) + public function getForeignKeyReferentialActionSQL(string $action): string { - throw Exception::notSupported(__METHOD__); - } + $upper = strtoupper($action); - /** - * @param string $name - * @param string $sql - * - * @return string - */ - public function getCreateViewSQL($name, $sql) - { - return 'CREATE VIEW ' . $name . ' AS ' . $sql; + return match ($upper) { + 'CASCADE', + 'SET NULL', + 'NO ACTION', + 'RESTRICT', + 'SET DEFAULT' => $upper, + default => throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $upper)), + }; } /** - * @param string $name - * - * @return string + * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint + * of a column declaration to be used in statements like CREATE TABLE. */ - public function getDropViewSQL($name) + public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey): string { - return 'DROP VIEW ' . $name; - } + $sql = ''; + if ($foreignKey->getName() !== '') { + $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; + } - /** - * @param string $sequence - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getSequenceNextValSQL($sequence) - { - throw Exception::notSupported(__METHOD__); - } + $sql .= 'FOREIGN KEY ('; - /** - * Returns the SQL to create a new database. - * - * @param string $name The name of the database that should be created. - * - * @return string - * - * @throws Exception If not supported on this platform. - */ - public function getCreateDatabaseSQL($name) - { - if (! $this->supportsCreateDropDatabase()) { - throw Exception::notSupported(__METHOD__); + if (count($foreignKey->getLocalColumns()) === 0) { + throw new InvalidArgumentException('Incomplete definition. "local" required.'); } - return 'CREATE DATABASE ' . $name; - } + if (count($foreignKey->getForeignColumns()) === 0) { + throw new InvalidArgumentException('Incomplete definition. "foreign" required.'); + } - /** - * Returns the SQL snippet to drop an existing database. - * - * @param string $name The name of the database that should be dropped. - * - * @return string - */ - public function getDropDatabaseSQL($name) - { - if (! $this->supportsCreateDropDatabase()) { - throw Exception::notSupported(__METHOD__); + if (strlen($foreignKey->getForeignTableName()) === 0) { + throw new InvalidArgumentException('Incomplete definition. "foreignTable" required.'); } - return 'DROP DATABASE ' . $name; + return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this)) + . ') REFERENCES ' + . $foreignKey->getQuotedForeignTableName($this) . ' (' + . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; } /** - * Returns the SQL to set the transaction isolation level. + * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET + * of a column declaration to be used in statements like CREATE TABLE. * - * @param int $level + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @return string + * @param string $charset The name of the charset. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set the CHARACTER SET + * of a column declaration. */ - public function getSetTransactionIsolationSQL($level) + public function getColumnCharsetDeclarationSQL(string $charset): string { - throw Exception::notSupported(__METHOD__); + return ''; } /** - * Obtains DBMS specific SQL to be used to create datetime columns in - * statements like CREATE TABLE. + * Obtains DBMS specific SQL code portion needed to set the COLLATION + * of a column declaration to be used in statements like CREATE TABLE. * - * @param mixed[] $column + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * - * @return string + * @param string $collation The name of the collation. * - * @throws Exception If not supported on this platform. + * @return string DBMS specific SQL code portion needed to set the COLLATION + * of a column declaration. */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getColumnCollationDeclarationSQL(string $collation): string { - throw Exception::notSupported(__METHOD__); + return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : ''; } /** - * Obtains DBMS specific SQL to be used to create datetime with timezone offset columns. + * Some platforms need the boolean values to be converted. * - * @param mixed[] $column + * The default conversion in this implementation converts to integers (false => 0, true => 1). + * + * Note: if the input is not a boolean the original input might be returned. + * + * There are two contexts when converting booleans: Literals and Prepared Statements. + * This method should handle the literal case + * + * @param mixed $item A boolean or an array of them. * - * @return string + * @return mixed A boolean database value or an array of them. */ - public function getDateTimeTzTypeDeclarationSQL(array $column) + public function convertBooleans(mixed $item): mixed { - return $this->getDateTimeTypeDeclarationSQL($column); + if (is_array($item)) { + foreach ($item as $k => $value) { + if (! is_bool($value)) { + continue; + } + + $item[$k] = (int) $value; + } + } elseif (is_bool($item)) { + $item = (int) $item; + } + + return $item; } /** - * Obtains DBMS specific SQL to be used to create date columns in statements - * like CREATE TABLE. + * Some platforms have boolean literals that needs to be correctly converted * - * @param mixed[] $column + * The default conversion tries to convert value into bool "(bool)$item" * - * @return string + * @param T $item + * + * @return (T is null ? null : bool) * - * @throws Exception If not supported on this platform. + * @template T */ - public function getDateTypeDeclarationSQL(array $column) + public function convertFromBoolean(mixed $item): ?bool { - throw Exception::notSupported(__METHOD__); + if ($item === null) { + return null; + } + + return (bool) $item; } /** - * Obtains DBMS specific SQL to be used to create time columns in statements - * like CREATE TABLE. + * This method should handle the prepared statements case. When there is no + * distinction, it's OK to use the same method. * - * @param mixed[] $column + * Note: if the input is not a boolean the original input might be returned. * - * @return string + * @param mixed $item A boolean or an array of them. * - * @throws Exception If not supported on this platform. + * @return mixed A boolean database value or an array of them. */ - public function getTimeTypeDeclarationSQL(array $column) + public function convertBooleansToDatabaseValue(mixed $item): mixed { - throw Exception::notSupported(__METHOD__); + return $this->convertBooleans($item); } /** - * @param mixed[] $column - * - * @return string + * Returns the SQL specific for the platform to get the current date. */ - public function getFloatDeclarationSQL(array $column) + public function getCurrentDateSQL(): string { - return 'DOUBLE PRECISION'; + return 'CURRENT_DATE'; } /** - * Gets the default transaction isolation level of the platform. - * - * @see TransactionIsolationLevel - * - * @return TransactionIsolationLevel::* The default isolation level. + * Returns the SQL specific for the platform to get the current time. */ - public function getDefaultTransactionIsolationLevel() + public function getCurrentTimeSQL(): string { - return TransactionIsolationLevel::READ_COMMITTED; + return 'CURRENT_TIME'; } - /* supports*() methods */ - /** - * Whether the platform supports sequences. - * - * @return bool + * Returns the SQL specific for the platform to get the current timestamp */ - public function supportsSequences() + public function getCurrentTimestampSQL(): string { - return false; + return 'CURRENT_TIMESTAMP'; } /** - * Whether the platform supports identity columns. - * - * Identity columns are columns that receive an auto-generated value from the - * database on insert of a row. - * - * @return bool + * Returns the SQL for a given transaction isolation level Connection constant. */ - public function supportsIdentityColumns() + protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string { - return false; + return match ($level) { + TransactionIsolationLevel::READ_UNCOMMITTED => 'READ UNCOMMITTED', + TransactionIsolationLevel::READ_COMMITTED => 'READ COMMITTED', + TransactionIsolationLevel::REPEATABLE_READ => 'REPEATABLE READ', + TransactionIsolationLevel::SERIALIZABLE => 'SERIALIZABLE', + }; } - /** - * Whether the platform emulates identity columns through sequences. - * - * Some platforms that do not support identity columns natively - * but support sequences can emulate identity columns by using - * sequences. - * - * @deprecated - * - * @return bool - */ - public function usesSequenceEmulatedIdentityColumns() + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListDatabasesSQL(): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); + throw NotSupported::new(__METHOD__); + } - return false; + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListSequencesSQL(string $database): string + { + throw NotSupported::new(__METHOD__); } /** - * Returns the name of the sequence for a particular identity column in a particular table. - * - * @deprecated - * - * @see usesSequenceEmulatedIdentityColumns - * - * @param string $tableName The name of the table to return the sequence name for. - * @param string $columnName The name of the identity column in the table to return the sequence name for. - * - * @return string + * Returns the SQL to list all views of a database or user. * - * @throws Exception If not supported on this platform. + * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ - public function getIdentitySequenceName($tableName, $columnName) + abstract public function getListViewsSQL(string $database): string; + + public function getCreateViewSQL(string $name, string $sql): string { - throw Exception::notSupported(__METHOD__); + return 'CREATE VIEW ' . $name . ' AS ' . $sql; } - /** - * Whether the platform supports indexes. - * - * @deprecated - * - * @return bool - */ - public function supportsIndexes() + public function getDropViewSQL(string $name): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsIndexes() is deprecated.', - ); + return 'DROP VIEW ' . $name; + } - return true; + public function getSequenceNextValSQL(string $sequence): string + { + throw NotSupported::new(__METHOD__); } /** - * Whether the platform supports partial indexes. + * Returns the SQL to create a new database. * - * @return bool + * @param string $name The name of the database that should be created. */ - public function supportsPartialIndexes() + public function getCreateDatabaseSQL(string $name): string { - return false; + return 'CREATE DATABASE ' . $name; } /** - * Whether the platform supports indexes with column length definitions. + * Returns the SQL snippet to drop an existing database. + * + * @param string $name The name of the database that should be dropped. */ - public function supportsColumnLengthIndexes(): bool + public function getDropDatabaseSQL(string $name): string { - return false; + return 'DROP DATABASE ' . $name; } /** - * Whether the platform supports altering tables. - * - * @deprecated All platforms must implement altering tables. - * - * @return bool + * Returns the SQL to set the transaction isolation level. */ - public function supportsAlterTable() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.', - ); - - return true; - } + abstract public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string; /** - * Whether the platform supports transactions. - * - * @deprecated + * Obtains DBMS specific SQL to be used to create datetime columns in + * statements like CREATE TABLE. * - * @return bool + * @param mixed[] $column */ - public function supportsTransactions() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsTransactions() is deprecated.', - ); - - return true; - } + abstract public function getDateTimeTypeDeclarationSQL(array $column): string; /** - * Whether the platform supports savepoints. + * Obtains DBMS specific SQL to be used to create datetime with timezone offset columns. * - * @return bool + * @param mixed[] $column */ - public function supportsSavepoints() + public function getDateTimeTzTypeDeclarationSQL(array $column): string { - return true; + return $this->getDateTimeTypeDeclarationSQL($column); } /** - * Whether the platform supports releasing savepoints. + * Obtains DBMS specific SQL to be used to create date columns in statements + * like CREATE TABLE. * - * @return bool + * @param mixed[] $column */ - public function supportsReleaseSavepoints() - { - return $this->supportsSavepoints(); - } + abstract public function getDateTypeDeclarationSQL(array $column): string; /** - * Whether the platform supports primary key constraints. - * - * @deprecated + * Obtains DBMS specific SQL to be used to create time columns in statements + * like CREATE TABLE. * - * @return bool + * @param mixed[] $column */ - public function supportsPrimaryConstraints() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.', - ); + abstract public function getTimeTypeDeclarationSQL(array $column): string; - return true; + /** @param mixed[] $column */ + public function getFloatDeclarationSQL(array $column): string + { + return 'DOUBLE PRECISION'; } /** - * Whether the platform supports foreign key constraints. - * - * @deprecated All platforms should support foreign key constraints. + * Gets the default transaction isolation level of the platform. * - * @return bool + * @return TransactionIsolationLevel The default isolation level. */ - public function supportsForeignKeyConstraints() + public function getDefaultTransactionIsolationLevel(): TransactionIsolationLevel { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5409', - 'AbstractPlatform::supportsForeignKeyConstraints() is deprecated.', - ); - - return true; + return TransactionIsolationLevel::READ_COMMITTED; } + /* supports*() methods */ + /** - * Whether the platform supports database schemas. - * - * @return bool + * Whether the platform supports sequences. */ - public function supportsSchemas() + public function supportsSequences(): bool { return false; } /** - * Whether this platform can emulate schemas. - * - * @deprecated - * - * Platforms that either support or emulate schemas don't automatically - * filter a schema for the namespaced elements in {@see AbstractManager::introspectSchema()}. + * Whether the platform supports identity columns. * - * @return bool + * Identity columns are columns that receive an auto-generated value from the + * database on insert of a row. */ - public function canEmulateSchemas() + public function supportsIdentityColumns(): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4805', - 'AbstractPlatform::canEmulateSchemas() is deprecated.', - ); - return false; } /** - * Returns the default schema name. - * - * @deprecated - * - * @return string + * Whether the platform supports partial indexes. * - * @throws Exception If not supported on this platform. + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getDefaultSchemaName() + public function supportsPartialIndexes(): bool { - throw Exception::notSupported(__METHOD__); + return false; } /** - * Whether this platform supports create database. - * - * Some databases don't allow to create and drop databases at all or only with certain tools. - * - * @deprecated - * - * @return bool + * Whether the platform supports indexes with column length definitions. */ - public function supportsCreateDropDatabase() + public function supportsColumnLengthIndexes(): bool { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return true; + return false; } /** - * Whether the platform supports getting the affected rows of a recent update/delete type query. - * - * @deprecated - * - * @return bool + * Whether the platform supports savepoints. */ - public function supportsGettingAffectedRows() + public function supportsSavepoints(): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.', - ); - return true; } /** - * Whether this platform support to add inline column comments as postfix. - * - * @return bool + * Whether the platform supports releasing savepoints. */ - public function supportsInlineColumnComments() + public function supportsReleaseSavepoints(): bool { - return false; + return $this->supportsSavepoints(); } /** - * Whether this platform support the proprietary syntax "COMMENT ON asset". - * - * @return bool + * Whether the platform supports database schemas. */ - public function supportsCommentOnStatement() + public function supportsSchemas(): bool { return false; } /** - * Does this platform have native guid type. - * - * @deprecated + * Whether this platform support to add inline column comments as postfix. * - * @return bool + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function hasNativeGuidType() + public function supportsInlineColumnComments(): bool { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - return false; } /** - * Does this platform have native JSON type. - * - * @deprecated + * Whether this platform support the proprietary syntax "COMMENT ON asset". * - * @return bool + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function hasNativeJsonType() + public function supportsCommentOnStatement(): bool { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - return false; } - /** - * Whether this platform supports views. - * - * @deprecated All platforms must implement support for views. - * - * @return bool - */ - public function supportsViews() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.', - ); - - return true; - } - /** * Does this platform support column collation? * - * @return bool + * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function supportsColumnCollation() + public function supportsColumnCollation(): bool { return false; } @@ -4329,7 +1980,7 @@ public function supportsColumnCollation() * * @return string The format string. */ - public function getDateTimeFormatString() + public function getDateTimeFormatString(): string { return 'Y-m-d H:i:s'; } @@ -4340,7 +1991,7 @@ public function getDateTimeFormatString() * * @return string The format string. */ - public function getDateTimeTzFormatString() + public function getDateTimeTzFormatString(): string { return 'Y-m-d H:i:s'; } @@ -4351,7 +2002,7 @@ public function getDateTimeTzFormatString() * * @return string The format string. */ - public function getDateFormatString() + public function getDateFormatString(): string { return 'Y-m-d'; } @@ -4362,53 +2013,30 @@ public function getDateFormatString() * * @return string The format string. */ - public function getTimeFormatString() + public function getTimeFormatString(): string { return 'H:i:s'; } /** * Adds an driver-specific LIMIT clause to the query. - * - * @param string $query - * @param int|null $limit - * @param int $offset - * - * @throws Exception */ - final public function modifyLimitQuery($query, $limit, $offset = 0): string + final public function modifyLimitQuery(string $query, ?int $limit, int $offset = 0): string { if ($offset < 0) { - throw new Exception(sprintf( - 'Offset must be a positive integer or zero, %d given', + throw new InvalidArgumentException(sprintf( + 'Offset must be a positive integer or zero, %d given.', $offset, )); } - if ($offset > 0 && ! $this->supportsLimitOffset()) { - throw new Exception(sprintf( - 'Platform %s does not support offset values in limit queries.', - $this->getName(), - )); - } - - if ($limit !== null) { - $limit = (int) $limit; - } - - return $this->doModifyLimitQuery($query, $limit, (int) $offset); + return $this->doModifyLimitQuery($query, $limit, $offset); } /** * Adds an platform-specific LIMIT clause to the query. - * - * @param string $query - * @param int|null $limit - * @param int $offset - * - * @return string */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string { if ($limit !== null) { $query .= sprintf(' LIMIT %d', $limit); @@ -4421,44 +2049,18 @@ protected function doModifyLimitQuery($query, $limit, $offset) return $query; } - /** - * Whether the database platform support offsets in modify limit clauses. - * - * @deprecated All platforms must implement support for offsets in modify limit clauses. - * - * @return bool - */ - public function supportsLimitOffset() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4724', - 'AbstractPlatform::supportsViews() is deprecated.' - . ' All platforms must implement support for offsets in modify limit clauses.', - ); - - return true; - } - /** * Maximum length of any given database identifier, like tables or column names. - * - * @return int */ - public function getMaxIdentifierLength() + public function getMaxIdentifierLength(): int { return 63; } /** * Returns the insert SQL for an empty insert statement. - * - * @param string $quotedTableName - * @param string $quotedIdentifierColumnName - * - * @return string */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + public function getEmptyIdentityInsertSQL(string $quotedTableName, string $quotedIdentifierColumnName): string { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (null)'; } @@ -4468,13 +2070,8 @@ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierCol * * Cascade is not supported on many platforms but would optionally cascade the truncate by * following the foreign keys. - * - * @param string $tableName - * @param bool $cascade - * - * @return string */ - public function getTruncateTableSQL($tableName, $cascade = false) + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string { $tableIdentifier = new Identifier($tableName); @@ -4483,56 +2080,38 @@ 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); } /** * Returns the SQL to create a new savepoint. - * - * @param string $savepoint - * - * @return string */ - public function createSavePoint($savepoint) + public function createSavePoint(string $savepoint): string { return 'SAVEPOINT ' . $savepoint; } /** * Returns the SQL to release a savepoint. - * - * @param string $savepoint - * - * @return string */ - public function releaseSavePoint($savepoint) + public function releaseSavePoint(string $savepoint): string { return 'RELEASE SAVEPOINT ' . $savepoint; } /** * Returns the SQL to rollback a savepoint. - * - * @param string $savepoint - * - * @return string */ - public function rollbackSavePoint($savepoint) + public function rollbackSavePoint(string $savepoint): string { return 'ROLLBACK TO SAVEPOINT ' . $savepoint; } /** * Returns the keyword list instance of this platform. - * - * @throws Exception If no keyword list is specified. */ final public function getReservedKeywordsList(): KeywordList { @@ -4542,43 +2121,8 @@ final public function getReservedKeywordsList(): KeywordList /** * Creates an instance of the reserved keyword list of this platform. - * - * This method will become @abstract in DBAL 4.0.0. - * - * @throws Exception - */ - protected function createReservedKeywordsList(): KeywordList - { - $class = $this->getReservedKeywordsClass(); - $keywords = new $class(); - if (! $keywords instanceof KeywordList) { - throw Exception::notSupported(__METHOD__); - } - - return $keywords; - } - - /** - * Returns the class name of the reserved keywords list. - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - * - * @return string - * @psalm-return class-string - * - * @throws Exception If not supported on this platform. */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'AbstractPlatform::getReservedKeywordsClass() is deprecated,' - . ' use AbstractPlatform::createReservedKeywordsList() instead.', - ); - - throw Exception::notSupported(__METHOD__); - } + abstract protected function createReservedKeywordsList(): KeywordList; /** * Quotes a literal string. @@ -4590,30 +2134,9 @@ protected function getReservedKeywordsClass() * * @return string The quoted literal string. */ - public function quoteStringLiteral($str) - { - $c = $this->getStringLiteralQuoteCharacter(); - - return $c . str_replace($c, $c . $c, $str) . $c; - } - - /** - * Gets the character used for string literal quoting. - * - * @deprecated Use {@see quoteStringLiteral()} to quote string literals instead. - * - * @return string - */ - public function getStringLiteralQuoteCharacter() + public function quoteStringLiteral(string $str): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5388', - 'AbstractPlatform::getStringLiteralQuoteCharacter() is deprecated.' - . ' Use quoteStringLiteral() instead.', - ); - - return "'"; + return "'" . str_replace("'", "''", $str) . "'"; } /** @@ -4626,11 +2149,15 @@ public function getStringLiteralQuoteCharacter() */ final public function escapeStringForLike(string $inputString, string $escapeChar): string { - return preg_replace( + $sql = preg_replace( '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u', addcslashes($escapeChar, '\\') . '$1', $inputString, ); + + assert(is_string($sql)); + + return $sql; } /** @@ -4639,12 +2166,10 @@ final public function escapeStringForLike(string $inputString, string $escapeCha */ private function columnToArray(Column $column): array { - $name = $column->getQuotedName($this); - return array_merge($column->toArray(), [ - 'name' => $name, + 'name' => $column->getQuotedName($this), 'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false, - 'comment' => $this->getColumnComment($column), + 'comment' => $column->getComment(), ]); } @@ -4661,8 +2186,6 @@ protected function getLikeWildcardCharacters(): string /** * Compares the definitions of the given columns in the context of this platform. - * - * @throws Exception */ public function columnsEqual(Column $column1, Column $column2): bool { @@ -4680,43 +2203,17 @@ public function columnsEqual(Column $column1, Column $column2): bool return false; } - if (! $this->columnDeclarationsMatch($column1, $column2)) { - return false; - } - // If the platform supports inline comments, all comparison is already done above if ($this->supportsInlineColumnComments()) { return true; } - if ($column1->getComment() !== $column2->getComment()) { - return false; - } - - return $column1->getType() === $column2->getType(); - } - - /** - * Whether the database data type matches that expected for the doctrine type for the given colunms. - */ - private function columnDeclarationsMatch(Column $column1, Column $column2): bool - { - return ! ( - $column1->hasPlatformOption('declarationMismatch') || - $column2->hasPlatformOption('declarationMismatch') - ); + return $column1->getComment() === $column2->getComment(); } /** * Creates the schema manager that can be used to inspect and change the underlying * database schema according to the dialect of the platform. - * - * @throws Exception - * - * @abstract */ - public function createSchemaManager(Connection $connection): AbstractSchemaManager - { - throw Exception::notSupported(__METHOD__); - } + abstract public function createSchemaManager(Connection $connection): AbstractSchemaManager; } diff --git a/doctrine/dbal/src/Platforms/DB2111Platform.php b/doctrine/dbal/src/Platforms/DB2111Platform.php deleted file mode 100644 index 40ab42f6e..000000000 --- a/doctrine/dbal/src/Platforms/DB2111Platform.php +++ /dev/null @@ -1,40 +0,0 @@ - 0) { - $query .= sprintf(' OFFSET %u ROWS', $offset); - } - - if ($limit !== null) { - if ($limit < 0) { - throw new Exception(sprintf('Limit must be a positive integer or zero, %d given', $limit)); - } - - $query .= sprintf(' FETCH %s %u ROWS ONLY', $offset === 0 ? 'FIRST' : 'NEXT', $limit); - } - - return $query; - } -} diff --git a/doctrine/dbal/src/Platforms/DB2Platform.php b/doctrine/dbal/src/Platforms/DB2Platform.php index 69234f410..a00b24bb7 100644 --- a/doctrine/dbal/src/Platforms/DB2Platform.php +++ b/doctrine/dbal/src/Platforms/DB2Platform.php @@ -1,9 +1,13 @@ getCharMaxLength(); - } - - return parent::getVarcharTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) + public function getBlobTypeDeclarationSQL(array $column): string { // todo blob(n) with $column['length']; return 'BLOB(1M)'; } - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings(): void { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, @@ -145,66 +61,20 @@ protected function initializeDoctrineTypeMappings() ]; } - /** - * {@inheritDoc} - */ - public function isCommentedDoctrineType(Type $doctrineType) + protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5058', - '%s() is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', - __METHOD__, - ); - - if ($doctrineType->getName() === Types::BOOLEAN) { - // We require a commented boolean type in order to distinguish between boolean and smallint - // as both (have to) map to the same native type. - return true; - } - - return parent::isCommentedDoctrineType($doctrineType); + return $this->getCharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA'; } - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default string column length on IBM DB2 is deprecated' - . ', specify the length explicitly.', - ); - } - - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(254)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + return $this->getVarcharTypeDeclarationSQLSnippet($length) . ' FOR BIT DATA'; } /** * {@inheritDoc} */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) - { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length on IBM DB2 is deprecated' - . ', specify the length explicitly.', - ); - } - - return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA'; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) + public function getClobTypeDeclarationSQL(array $column): string { // todo clob(n) with $column['length']; return 'CLOB(1M)'; @@ -213,22 +83,7 @@ public function getClobTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getName() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4749', - '%s() is deprecated. Identify platforms by their class.', - __METHOD__, - ); - - return 'db2'; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) + public function getBooleanTypeDeclarationSQL(array $column): string { return 'SMALLINT'; } @@ -236,7 +91,7 @@ public function getBooleanTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getIntegerTypeDeclarationSQL(array $column) + public function getIntegerTypeDeclarationSQL(array $column): string { return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -244,7 +99,7 @@ public function getIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBigIntTypeDeclarationSQL(array $column) + public function getBigIntTypeDeclarationSQL(array $column): string { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -252,7 +107,7 @@ public function getBigIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getSmallIntTypeDeclarationSQL(array $column) + public function getSmallIntTypeDeclarationSQL(array $column): string { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -260,7 +115,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string { $autoinc = ''; if (! empty($column['autoincrement'])) { @@ -270,46 +125,38 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) return $autoinc; } - /** - * {@inheritDoc} - */ - public function getBitAndComparisonExpression($value1, $value2) + public function getBitAndComparisonExpression(string $value1, string $value2): string { return 'BITAND(' . $value1 . ', ' . $value2 . ')'; } - /** - * {@inheritDoc} - */ - public function getBitOrComparisonExpression($value1, $value2) + public function getBitOrComparisonExpression(string $value1, string $value2): string { return 'BITOR(' . $value1 . ', ' . $value2 . ')'; } - /** - * {@inheritDoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { + protected function getDateArithmeticIntervalExpression( + string $date, + string $operator, + string $interval, + DateIntervalUnit $unit, + ): string { switch ($unit) { case DateIntervalUnit::WEEK: - $interval = $this->multiplyInterval((string) $interval, 7); + $interval = $this->multiplyInterval($interval, 7); $unit = DateIntervalUnit::DAY; break; case DateIntervalUnit::QUARTER: - $interval = $this->multiplyInterval((string) $interval, 3); + $interval = $this->multiplyInterval($interval, 3); $unit = DateIntervalUnit::MONTH; break; } - return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; + return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit->value; } - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) + public function getDateDiffExpression(string $date1, string $date2): string { return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; } @@ -317,7 +164,7 @@ public function getDateDiffExpression($date1, $date2) /** * {@inheritDoc} */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getDateTimeTypeDeclarationSQL(array $column): string { if (isset($column['version']) && $column['version'] === true) { return 'TIMESTAMP(0) WITH DEFAULT'; @@ -329,7 +176,7 @@ public function getDateTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTypeDeclarationSQL(array $column) + public function getDateTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -337,231 +184,61 @@ public function getDateTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getTimeTypeDeclarationSQL(array $column) + public function getTimeTypeDeclarationSQL(array $column): string { return 'TIME'; } - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE'; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. - * - * @param string $table - * @param string $database - * - * @return string - */ - public function getListTableColumnsSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - // We do the funky subquery and join syscat.columns.default this crazy way because - // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, - // it wants shorter stuff like a varchar. - return " - SELECT - cols.default, - subq.* - FROM ( - SELECT DISTINCT - c.tabschema, - c.tabname, - c.colname, - c.colno, - c.typename, - c.codepage, - c.nulls, - c.length, - c.scale, - c.identity, - tc.type AS tabconsttype, - c.remarks AS comment, - k.colseq, - CASE - WHEN c.generated = '" . self::SYSCAT_COLUMNS_GENERATED_DEFAULT . "' THEN 1 - ELSE 0 - END AS autoincrement - FROM syscat.columns c - LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc - ON (k.tabschema = tc.tabschema - AND k.tabname = tc.tabname - AND tc.type = '" . self::SYSCAT_TABCONST_TYPE_PRIMARY_KEY . "')) - ON (c.tabschema = k.tabschema - AND c.tabname = k.tabname - AND c.colname = k.colname) - WHERE UPPER(c.tabname) = UPPER(" . $table . ') - ORDER BY c.colno - ) subq - JOIN syscat.columns cols - ON subq.tabschema = cols.tabschema - AND subq.tabname = cols.tabname - AND subq.colno = cols.colno - ORDER BY subq.colno - '; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTablesSQL() + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string { - return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = '" . self::SYSIBM_SYSTABLES_TYPE_TABLE . "'" - . ' AND CREATOR = CURRENT_USER'; + throw NotSupported::new(__METHOD__); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string { return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS'; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - $table = $this->quoteStringLiteral($table); - - return "SELECT idx.INDNAME AS key_name, - idxcol.COLNAME AS column_name, - CASE - WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_IMPLEMENTS_PRIMARY_KEY . "' - THEN 1 - ELSE 0 - END AS primary, - CASE - WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_PERMITS_DUPLICATES . "' - THEN 1 - ELSE 0 - END AS non_unique - FROM SYSCAT.INDEXES AS idx - JOIN SYSCAT.INDEXCOLUSE AS idxcol - ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME - WHERE idx.TABNAME = UPPER(" . $table . ') - ORDER BY idxcol.COLSEQ ASC'; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableForeignKeysSQL($table) - { - $table = $this->quoteStringLiteral($table); - - return "SELECT fkcol.COLNAME AS local_column, - fk.REFTABNAME AS foreign_table, - pkcol.COLNAME AS foreign_column, - fk.CONSTNAME AS index_name, - CASE - WHEN fk.UPDATERULE = '" . self::SYSCAT_REFERENCES_UPDATERULE_RESTRICT . "' THEN 'RESTRICT' - ELSE NULL - END AS on_update, - CASE - WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_CASCADE . "' THEN 'CASCADE' - WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_SET_NULL . "' THEN 'SET NULL' - WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_RESTRICT . "' THEN 'RESTRICT' - ELSE NULL - END AS on_delete - FROM SYSCAT.REFERENCES AS fk - JOIN SYSCAT.KEYCOLUSE AS fkcol - ON fk.CONSTNAME = fkcol.CONSTNAME - AND fk.TABSCHEMA = fkcol.TABSCHEMA - AND fk.TABNAME = fkcol.TABNAME - JOIN SYSCAT.KEYCOLUSE AS pkcol - ON fk.REFKEYNAME = pkcol.CONSTNAME - AND fk.REFTABSCHEMA = pkcol.TABSCHEMA - AND fk.REFTABNAME = pkcol.TABNAME - WHERE fk.TABNAME = UPPER(" . $table . ') - ORDER BY fkcol.COLSEQ ASC'; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function supportsCreateDropDatabase() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s() is deprecated.', - __METHOD__, - ); - - return false; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsCommentOnStatement() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsCommentOnStatement(): bool { return true; } - /** - * {@inheritDoc} - */ - public function getCurrentDateSQL() + public function getCurrentDateSQL(): string { return 'CURRENT DATE'; } - /** - * {@inheritDoc} - */ - public function getCurrentTimeSQL() + public function getCurrentTimeSQL(): string { return 'CURRENT TIME'; } - /** - * {@inheritDoc} - */ - public function getCurrentTimestampSQL() + public function getCurrentTimestampSQL(): string { return 'CURRENT TIMESTAMP'; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getIndexDeclarationSQL($name, Index $index) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getIndexDeclarationSQL(Index $index): string { // Index declaration in statements like CREATE TABLE is not supported. - throw Exception::notSupported(__METHOD__); + throw NotSupported::new(__METHOD__); } /** * {@inheritDoc} */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { $indexes = []; if (isset($options['indexes'])) { @@ -582,20 +259,16 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] /** * {@inheritDoc} */ - public function getAlterTableSQL(TableDiff $diff) + public function getAlterTableSQL(TableDiff $diff): array { $sql = []; $columnSql = []; $commentsSQL = []; - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); $queryParts = []; foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - $columnDef = $column->toArray(); $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); @@ -610,9 +283,9 @@ public function getAlterTableSQL(TableDiff $diff) $queryParts[] = $queryPart; - $comment = $this->getColumnComment($column); + $comment = $column->getComment(); - if ($comment === null || $comment === '') { + if ($comment === '') { continue; } @@ -624,23 +297,16 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getDroppedColumns() as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - if ($columnDiff->hasCommentChanged()) { + $newColumn = $columnDiff->getNewColumn(); $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, - $columnDiff->getNewColumn()->getQuotedName($this), - $this->getColumnComment($columnDiff->getNewColumn()), + $newColumn->getQuotedName($this), + $newColumn->getComment(), ); } @@ -653,80 +319,49 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - $oldColumnName = new Identifier($oldColumnName); $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if (count($queryParts) > 0) { - $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . implode(' ', $queryParts); - } - - // Some table alteration operations require a table reorganization. - if (count($diff->getDroppedColumns()) > 0 || count($diff->getModifiedColumns()) > 0) { - $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $tableNameSQL . "')"; - } - - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of "rename table" SQL using %s() is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $sql[] = sprintf( - 'RENAME TABLE %s TO %s', - $tableNameSQL, - $newName->getQuotedName($this), - ); - } + if (count($queryParts) > 0) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . implode(' ', $queryParts); + } - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff), - ); + // Some table alteration operations require a table reorganization. + if (count($diff->getDroppedColumns()) > 0 || count($diff->getModifiedColumns()) > 0) { + $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $tableNameSQL . "')"; } - return array_merge($sql, $tableSql, $columnSql); + $sql = array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $commentsSQL, + $this->getPostAlterTableIndexForeignKeySQL($diff), + ); + + return array_merge($sql, $columnSql); } - /** - * {@inheritDoc} - */ - public function getRenameTableSQL(string $oldName, string $newName): array + public function getRenameTableSQL(string $oldName, string $newName): string { - return [ - sprintf('RENAME TABLE %s TO %s', $oldName, $newName), - ]; + return sprintf('RENAME TABLE %s TO %s', $oldName, $newName); } /** * Gathers the table alteration SQL for a given column diff. * - * @param string $table The table to gather the SQL for. - * @param ColumnDiff $columnDiff The column diff to evaluate. - * @param string[] $sql The sequence of table alteration statements to fill. - * @param mixed[] $queryParts The sequence of column alteration clauses to fill. + * @param string $table The table to gather the SQL for. + * @param ColumnDiff $columnDiff The column diff to evaluate. + * @param list $sql The sequence of table alteration statements to fill. + * @param list $queryParts The sequence of column alteration clauses to fill. */ private function gatherAlterColumnSQL( string $table, ColumnDiff $columnDiff, array &$sql, - array &$queryParts + array &$queryParts, ): void { $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); @@ -798,11 +433,11 @@ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff): array /** * {@inheritDoc} */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array { $sql = []; - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); foreach ($diff->getDroppedIndexes() as $droppedIndex) { foreach ($diff->getAddedIndexes() as $addedIndex) { @@ -815,7 +450,7 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) } elseif ($droppedIndex->isUnique()) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP UNIQUE ' . $droppedIndex->getQuotedName($this); } else { - $sql[] = $this->getDropIndexSQL($droppedIndex, $tableNameSQL); + $sql[] = $this->getDropIndexSQL($droppedIndex->getQuotedName($this), $tableNameSQL); } $sql[] = $this->getCreateIndexSQL($addedIndex, $tableNameSQL); @@ -833,9 +468,9 @@ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) /** * {@inheritDoc} */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array { - if (strpos($tableName, '.') !== false) { + if (str_contains($tableName, '.')) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } @@ -848,7 +483,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getDefaultValueDeclarationSQL($column) + public function getDefaultValueDeclarationSQL(array $column): string { if (! empty($column['autoincrement'])) { return ''; @@ -863,87 +498,55 @@ public function getDefaultValueDeclarationSQL($column) return parent::getDefaultValueDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + public function getEmptyIdentityInsertSQL(string $quotedTableName, string $quotedIdentifierColumnName): string { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; } - /** - * {@inheritDoc} - */ - public function getCreateTemporaryTableSnippetSQL() + public function getCreateTemporaryTableSnippetSQL(): string { return 'DECLARE GLOBAL TEMPORARY TABLE'; } - /** - * {@inheritDoc} - */ - public function getTemporaryTableName($tableName) + public function getTemporaryTableName(string $tableName): string { return 'SESSION.' . $tableName; } - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string { - $where = []; - if ($offset > 0) { - $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1); + $query .= sprintf(' OFFSET %d ROWS', $offset); } if ($limit !== null) { - $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit); - } - - if (empty($where)) { - return $query; + $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); } - // Todo OVER() needs ORDER BY data! - return sprintf( - 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s', - $query, - implode(' AND ', $where), - ); + return $query; } - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) + public function getLocateExpression(string $string, string $substring, ?string $start = null): string { - if ($startPos === false) { - return 'LOCATE(' . $substr . ', ' . $str . ')'; + if ($start === null) { + return sprintf('LOCATE(%s, %s)', $substring, $string); } - return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + return sprintf('LOCATE(%s, %s, %s)', $substring, $string, $start); } - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) + public function getSubstringExpression(string $string, string $start, ?string $length = null): string { if ($length === null) { - return 'SUBSTR(' . $string . ', ' . $start . ')'; + return sprintf('SUBSTR(%s, %s)', $string, $start); } - return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; + return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length); } - /** - * {@inheritDoc} - */ - public function getLengthExpression($column) + public function getLengthExpression(string $string): string { - return 'LENGTH(' . $column . ', CODEUNITS32)'; + return 'LENGTH(' . $string . ', CODEUNITS32)'; } public function getCurrentDatabaseExpression(): string @@ -951,28 +554,8 @@ public function getCurrentDatabaseExpression(): string return 'CURRENT_USER'; } - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function prefersIdentityColumns() + public function supportsIdentityColumns(): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/1519', - '%s() is deprecated.', - __METHOD__, - ); - return true; } @@ -981,23 +564,8 @@ public function createSelectSQLBuilder(): SelectSQLBuilder return new DefaultSelectSQLBuilder($this, 'WITH RR USE AND KEEP UPDATE LOCKS', null); } - /** - * {@inheritDoc} - * - * @deprecated This API is not portable. - */ - public function getForUpdateSQL() - { - return ' WITH RR USE AND KEEP UPDATE LOCKS'; - } - - /** - * {@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); } @@ -1008,42 +576,14 @@ public function getDummySelectSQL() * * TODO: We have to investigate how to get DB2 up and running with savepoints. */ - public function supportsSavepoints() + public function supportsSavepoints(): bool { return false; } - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() + protected function createReservedKeywordsList(): KeywordList { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - '%s() is deprecated,' - . ' use %s::createReservedKeywordsList() instead.', - __METHOD__, - static::class, - ); - - return Keywords\DB2Keywords::class; - } - - /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ - public function getListTableCommentsSQL(string $table): string - { - return sprintf( - <<<'SQL' -SELECT REMARKS - FROM SYSIBM.SYSTABLES - WHERE NAME = UPPER( %s ) -SQL - , - $this->quoteStringLiteral($table), - ); + return new DB2Keywords(); } public function createSchemaManager(Connection $connection): DB2SchemaManager diff --git a/doctrine/dbal/src/Platforms/DateIntervalUnit.php b/doctrine/dbal/src/Platforms/DateIntervalUnit.php index a95c4e28b..ba783f372 100644 --- a/doctrine/dbal/src/Platforms/DateIntervalUnit.php +++ b/doctrine/dbal/src/Platforms/DateIntervalUnit.php @@ -4,26 +4,14 @@ namespace Doctrine\DBAL\Platforms; -final class DateIntervalUnit +enum DateIntervalUnit: string { - public const SECOND = 'SECOND'; - - public const MINUTE = 'MINUTE'; - - public const HOUR = 'HOUR'; - - public const DAY = 'DAY'; - - public const WEEK = 'WEEK'; - - public const MONTH = 'MONTH'; - - public const QUARTER = 'QUARTER'; - - public const YEAR = 'YEAR'; - - /** @codeCoverageIgnore */ - private function __construct() - { - } + case SECOND = 'SECOND'; + case MINUTE = 'MINUTE'; + case HOUR = 'HOUR'; + case DAY = 'DAY'; + case WEEK = 'WEEK'; + case MONTH = 'MONTH'; + case QUARTER = 'QUARTER'; + case YEAR = 'YEAR'; } diff --git a/doctrine/dbal/src/Platforms/Exception/InvalidPlatformVersion.php b/doctrine/dbal/src/Platforms/Exception/InvalidPlatformVersion.php new file mode 100644 index 000000000..dd701905a --- /dev/null +++ b/doctrine/dbal/src/Platforms/Exception/InvalidPlatformVersion.php @@ -0,0 +1,28 @@ +keywords === null) { $this->initializeKeywords(); @@ -32,8 +28,7 @@ public function isKeyword($word) return isset($this->keywords[strtoupper($word)]); } - /** @return void */ - protected function initializeKeywords() + protected function initializeKeywords(): void { $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); } @@ -43,14 +38,5 @@ protected function initializeKeywords() * * @return string[] */ - abstract protected function getKeywords(); - - /** - * Returns the name of this keyword list. - * - * @deprecated - * - * @return string - */ - abstract public function getName(); + abstract protected function getKeywords(): array; } diff --git a/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php index 5417c6caa..6857cd31e 100644 --- a/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php +++ b/doctrine/dbal/src/Platforms/Keywords/MariaDBKeywords.php @@ -1,23 +1,11 @@ keywordLists = $keywordLists; - } - - /** @return string[] */ - public function getViolations() - { - return $this->violations; - } - - /** - * @param string $word - * - * @return string[] - */ - private function isReservedWord($word): array - { - if ($word[0] === '`') { - $word = str_replace('`', '', $word); - } - - $keywordLists = []; - foreach ($this->keywordLists as $keywordList) { - if (! $keywordList->isKeyword($word)) { - continue; - } - - $keywordLists[] = $keywordList->getName(); - } - - return $keywordLists; - } - - /** - * @param string $asset - * @param string[] $violatedPlatforms - */ - private function addViolation($asset, $violatedPlatforms): void - { - if (count($violatedPlatforms) === 0) { - return; - } - - $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); - } - - /** - * {@inheritDoc} - */ - public function acceptColumn(Table $table, Column $column) - { - $this->addViolation( - 'Table ' . $table->getName() . ' column ' . $column->getName(), - $this->isReservedWord($column->getName()), - ); - } - - /** - * {@inheritDoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - } - - /** - * {@inheritDoc} - */ - public function acceptIndex(Table $table, Index $index) - { - } - - /** - * {@inheritDoc} - */ - public function acceptSchema(Schema $schema) - { - } - - /** - * {@inheritDoc} - */ - public function acceptSequence(Sequence $sequence) - { - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - $this->addViolation( - 'Table ' . $table->getName(), - $this->isReservedWord($table->getName()), - ); - } -} diff --git a/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php b/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php deleted file mode 100644 index ebc45c402..000000000 --- a/doctrine/dbal/src/Platforms/Keywords/SQLServer2012Keywords.php +++ /dev/null @@ -1,12 +0,0 @@ -getQuotedName($this)]; } diff --git a/doctrine/dbal/src/Platforms/MariaDb1060Platform.php b/doctrine/dbal/src/Platforms/MariaDB1060Platform.php similarity index 74% rename from doctrine/dbal/src/Platforms/MariaDb1060Platform.php rename to doctrine/dbal/src/Platforms/MariaDB1060Platform.php index 82d11f21d..028473d46 100644 --- a/doctrine/dbal/src/Platforms/MariaDb1060Platform.php +++ b/doctrine/dbal/src/Platforms/MariaDB1060Platform.php @@ -1,13 +1,15 @@ quoteStringLiteral($databaseName); + + // The check for `CONSTRAINT_SCHEMA = $databaseName` is mandatory here to prevent performance issues + return <<getOldTable()->getQuotedName($this); + + $modifiedForeignKeys = $diff->getModifiedForeignKeys(); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $modifiedForeignKeys, true)) { + continue; + } + + $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableName); + } + + return $sql; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff): array + { + return array_merge( + parent::getPostAlterTableIndexForeignKeySQL($diff), + $this->getPostAlterTableRenameIndexForeignKeySQL($diff), + ); + } + + /** @return list */ + private function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff): array { - return 'LONGTEXT'; + $sql = []; + + $tableName = $diff->getOldTable()->getQuotedName($this); + + $modifiedForeignKeys = $diff->getModifiedForeignKeys(); + + foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { + if (in_array($foreignKey, $modifiedForeignKeys, true)) { + continue; + } + + $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableName); + } + + return $sql; } - /** @deprecated Implement {@see createReservedKeywordsList()} instead. */ - protected function getReservedKeywordsClass(): string + /** + * Returns the remaining foreign key constraints that require one of the renamed indexes. + * + * "Remaining" here refers to the diff between the foreign keys currently defined in the associated + * table and the foreign keys to be removed. + * + * @param TableDiff $diff The table diff to evaluate. + * + * @return ForeignKeyConstraint[] + */ + private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff): array { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'MariaDb1027Platform::getReservedKeywordsClass() is deprecated,' - . ' use MariaDb1027Platform::createReservedKeywordsList() instead.', + $renamedIndexes = $diff->getRenamedIndexes(); + + if (count($renamedIndexes) === 0) { + return []; + } + + $foreignKeys = []; + + $remainingForeignKeys = array_diff_key( + $diff->getOldTable()->getForeignKeys(), + $diff->getDroppedForeignKeys(), ); - return Keywords\MariaDb102Keywords::class; + foreach ($remainingForeignKeys as $foreignKey) { + foreach ($renamedIndexes as $index) { + if ($foreignKey->intersectsIndexColumns($index)) { + $foreignKeys[] = $foreignKey; + + break; + } + } + } + + return $foreignKeys; } - protected function initializeDoctrineTypeMappings(): void + /** {@inheritDoc} */ + public function getColumnDeclarationSQL(string $name, array $column): string { - parent::initializeDoctrineTypeMappings(); + // MariaDb forces column collation to utf8mb4_bin where the column was declared as JSON so ignore + // collation and character set for json columns as attempting to set them can cause an error. + if ($this->getJsonTypeDeclarationSQL([]) === 'JSON' && ($column['type'] ?? null) instanceof JsonType) { + unset($column['collation']); + unset($column['charset']); + } + + return parent::getColumnDeclarationSQL($name, $column); + } - $this->doctrineTypeMapping['json'] = Types::JSON; + protected function createReservedKeywordsList(): KeywordList + { + return new MariaDBKeywords(); } } diff --git a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php b/doctrine/dbal/src/Platforms/MariaDb1027Platform.php deleted file mode 100644 index 68f381332..000000000 --- a/doctrine/dbal/src/Platforms/MariaDb1027Platform.php +++ /dev/null @@ -1,15 +0,0 @@ -getColumnTypeSQLSnippets('c', $database); - - return sprintf( - <<getDatabaseNameSQL($database), - $this->quoteStringLiteral($table), - ); - } - - /** - * Generate SQL snippets to reverse the aliasing of JSON to LONGTEXT. - * - * MariaDb aliases columns specified as JSON to LONGTEXT and sets a CHECK constraint to ensure the column - * is valid json. This function generates the SQL snippets which reverse this aliasing i.e. report a column - * as JSON where it was originally specified as such instead of LONGTEXT. - * - * The CHECK constraints are stored in information_schema.CHECK_CONSTRAINTS so query that table. - */ - public function getColumnTypeSQLSnippet(string $tableAlias = 'c', ?string $databaseName = null): string - { - if ($this->getJsonTypeDeclarationSQL([]) !== 'JSON') { - return parent::getColumnTypeSQLSnippet($tableAlias, $databaseName); - } - - if ($databaseName === null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6215', - 'Not passing a database name to methods "getColumnTypeSQLSnippet()", ' - . '"getColumnTypeSQLSnippets()", and "getListTableColumnsSQL()" of "%s" is deprecated.', - self::class, - ); - } - - $subQueryAlias = 'i_' . $tableAlias; - - $databaseName = $this->getDatabaseNameSQL($databaseName); - - // The check for `CONSTRAINT_SCHEMA = $databaseName` is mandatory here to prevent performance issues - return <<getJsonTypeDeclarationSQL([]) === 'JSON' && ($column['type'] ?? null) instanceof JsonType) { - unset($column['collation']); - unset($column['charset']); - } - - return parent::getColumnDeclarationSQL($name, $column); - } -} diff --git a/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider.php new file mode 100644 index 000000000..665543e0d --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider.php @@ -0,0 +1,11 @@ + */ + private array $cache = []; + + public function __construct(private readonly CharsetMetadataProvider $charsetMetadataProvider) + { + } + + public function getDefaultCharsetCollation(string $charset): ?string + { + if (array_key_exists($charset, $this->cache)) { + return $this->cache[$charset]; + } + + return $this->cache[$charset] = $this->charsetMetadataProvider->getDefaultCharsetCollation($charset); + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/ConnectionCharsetMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/ConnectionCharsetMetadataProvider.php new file mode 100644 index 000000000..65b63df8f --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/CharsetMetadataProvider/ConnectionCharsetMetadataProvider.php @@ -0,0 +1,37 @@ +connection->fetchOne( + <<<'SQL' + SELECT DEFAULT_COLLATE_NAME + FROM information_schema.CHARACTER_SETS + WHERE CHARACTER_SET_NAME = ?; + SQL + , + [$charset], + ); + + if ($collation !== false) { + return $collation; + } + + return null; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php index 513c08b6e..0c99aa313 100644 --- a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.php @@ -11,15 +11,11 @@ /** @internal */ final class CachingCollationMetadataProvider implements CollationMetadataProvider { - /** @var CollationMetadataProvider */ - private $collationMetadataProvider; - /** @var array */ - private $cache = []; + private array $cache = []; - public function __construct(CollationMetadataProvider $collationMetadataProvider) + public function __construct(private readonly CollationMetadataProvider $collationMetadataProvider) { - $this->collationMetadataProvider = $collationMetadataProvider; } public function getCollationCharset(string $collation): ?string diff --git a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php index 8dc2421a0..fcd999562 100644 --- a/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php +++ b/doctrine/dbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.php @@ -11,12 +11,8 @@ /** @internal */ final class ConnectionCollationMetadataProvider implements CollationMetadataProvider { - /** @var Connection */ - private $connection; - - public function __construct(Connection $connection) + public function __construct(private readonly Connection $connection) { - $this->connection = $connection; } /** @throws Exception */ diff --git a/doctrine/dbal/src/Platforms/MySQL/Comparator.php b/doctrine/dbal/src/Platforms/MySQL/Comparator.php index c27fef744..ebe025dc2 100644 --- a/doctrine/dbal/src/Platforms/MySQL/Comparator.php +++ b/doctrine/dbal/src/Platforms/MySQL/Comparator.php @@ -1,5 +1,7 @@ collationMetadataProvider = $collationMetadataProvider; } - public function compareTables(Table $fromTable, Table $toTable): TableDiff + public function compareTables(Table $oldTable, Table $newTable): TableDiff { return parent::compareTables( - $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable), + $this->normalizeTable($oldTable), + $this->normalizeTable($newTable), ); } - /** - * {@inheritDoc} - */ - public function diffTable(Table $fromTable, Table $toTable) + private function normalizeTable(Table $table): Table { - return parent::diffTable( - $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable), - ); - } + $charset = $table->getOption('charset'); + $collation = $table->getOption('collation'); + + if ($charset === null && $collation !== null) { + $charset = $this->collationMetadataProvider->getCollationCharset($collation); + } elseif ($charset !== null && $collation === null) { + $collation = $this->charsetMetadataProvider->getDefaultCharsetCollation($charset); + } elseif ($charset === null && $collation === null) { + $charset = $this->defaultTableOptions->getCharset(); + $collation = $this->defaultTableOptions->getCollation(); + } - private function normalizeColumns(Table $table): Table - { - $tableOptions = array_intersect_key($table->getOptions(), [ - 'charset' => null, - 'collation' => null, - ]); + $tableOptions = [ + 'charset' => $charset, + 'collation' => $collation, + ]; $table = clone $table; @@ -77,16 +78,14 @@ private function normalizeColumns(Table $table): Table /** * @param array $options * - * @return array + * @return array */ private function normalizeOptions(array $options): array { - if (isset($options['collation']) && ! isset($options['charset'])) { - $charset = $this->collationMetadataProvider->getCollationCharset($options['collation']); - - if ($charset !== null) { - $options['charset'] = $charset; - } + if (isset($options['charset']) && ! isset($options['collation'])) { + $options['collation'] = $this->charsetMetadataProvider->getDefaultCharsetCollation($options['charset']); + } elseif (isset($options['collation']) && ! isset($options['charset'])) { + $options['charset'] = $this->collationMetadataProvider->getCollationCharset($options['collation']); } return $options; diff --git a/doctrine/dbal/src/Platforms/MySQL/DefaultTableOptions.php b/doctrine/dbal/src/Platforms/MySQL/DefaultTableOptions.php new file mode 100644 index 000000000..ede3ba23b --- /dev/null +++ b/doctrine/dbal/src/Platforms/MySQL/DefaultTableOptions.php @@ -0,0 +1,23 @@ +charset; + } + + public function getCollation(): string + { + return $this->collation; + } +} diff --git a/doctrine/dbal/src/Platforms/MySQL57Platform.php b/doctrine/dbal/src/Platforms/MySQL57Platform.php deleted file mode 100644 index f3899959d..000000000 --- a/doctrine/dbal/src/Platforms/MySQL57Platform.php +++ /dev/null @@ -1,99 +0,0 @@ -getQuotedName($this)]; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'MySQL57Platform::getReservedKeywordsClass() is deprecated,' - . ' use MySQL57Platform::createReservedKeywordsList() instead.', - ); - - return Keywords\MySQL57Keywords::class; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - parent::initializeDoctrineTypeMappings(); - - $this->doctrineTypeMapping['json'] = Types::JSON; - } -} diff --git a/doctrine/dbal/src/Platforms/MySQL80Platform.php b/doctrine/dbal/src/Platforms/MySQL80Platform.php index 5ab353848..14a81a635 100644 --- a/doctrine/dbal/src/Platforms/MySQL80Platform.php +++ b/doctrine/dbal/src/Platforms/MySQL80Platform.php @@ -1,30 +1,21 @@ getQuotedName($this)]; + } + + protected function createReservedKeywordsList(): KeywordList + { + return new MySQLKeywords(); + } } diff --git a/doctrine/dbal/src/Platforms/OraclePlatform.php b/doctrine/dbal/src/Platforms/OraclePlatform.php index eae13ee73..314f6ee7d 100644 --- a/doctrine/dbal/src/Platforms/OraclePlatform.php +++ b/doctrine/dbal/src/Platforms/OraclePlatform.php @@ -1,32 +1,30 @@ multiplyInterval((string) $interval, 3); + $interval = $this->multiplyInterval($interval, 3); break; case DateIntervalUnit::YEAR: - $interval = $this->multiplyInterval((string) $interval, 12); + $interval = $this->multiplyInterval($interval, 12); break; } @@ -149,18 +98,12 @@ protected function getDateArithmeticIntervalExpression($date, $operator, $interv } } - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) + public function getDateDiffExpression(string $date1, string $date2): string { return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2); } - /** - * {@inheritDoc} - */ - public function getBitAndComparisonExpression($value1, $value2) + public function getBitAndComparisonExpression(string $value1, string $value2): string { return 'BITAND(' . $value1 . ', ' . $value2 . ')'; } @@ -170,34 +113,17 @@ public function getCurrentDatabaseExpression(): string return "SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')"; } - /** - * {@inheritDoc} - */ - public function getBitOrComparisonExpression($value1, $value2) + public function getBitOrComparisonExpression(string $value1, string $value2): string { return '(' . $value1 . '-' . $this->getBitAndComparisonExpression($value1, $value2) . '+' . $value2 . ')'; } - /** - * {@inheritDoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table): string + public function getCreatePrimaryKeySQL(Index $index, string $table): string { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this) - . ' PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + . ' PRIMARY KEY (' . implode(', ', $index->getQuotedColumns($this)) . ')'; } /** @@ -207,7 +133,7 @@ public function getCreatePrimaryKeySQL(Index $index, $table): string * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection * in {@see listSequences()} */ - public function getCreateSequenceSQL(Sequence $sequence) + public function getCreateSequenceSQL(Sequence $sequence): string { return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . ' START WITH ' . $sequence->getInitialValue() . @@ -216,10 +142,7 @@ public function getCreateSequenceSQL(Sequence $sequence) $this->getSequenceCacheSQL($sequence); } - /** - * {@inheritDoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) + public function getAlterSequenceSQL(Sequence $sequence): string { return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() @@ -246,47 +169,30 @@ private function getSequenceCacheSQL(Sequence $sequence): string return ''; } - /** - * {@inheritDoc} - */ - public function getSequenceNextValSQL($sequence) + public function getSequenceNextValSQL(string $sequence): string { return 'SELECT ' . $sequence . '.nextval FROM DUAL'; } - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string { return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } - /** - * {@inheritDoc} - */ - protected function _getTransactionIsolationLevelSQL($level) + protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string { - switch ($level) { - case TransactionIsolationLevel::READ_UNCOMMITTED: - return 'READ UNCOMMITTED'; - - case TransactionIsolationLevel::READ_COMMITTED: - return 'READ COMMITTED'; - - case TransactionIsolationLevel::REPEATABLE_READ: - case TransactionIsolationLevel::SERIALIZABLE: - return 'SERIALIZABLE'; - - default: - return parent::_getTransactionIsolationLevelSQL($level); - } + return match ($level) { + TransactionIsolationLevel::READ_UNCOMMITTED => 'READ UNCOMMITTED', + TransactionIsolationLevel::READ_COMMITTED => 'READ COMMITTED', + TransactionIsolationLevel::REPEATABLE_READ, + TransactionIsolationLevel::SERIALIZABLE => 'SERIALIZABLE', + }; } /** * {@inheritDoc} */ - public function getBooleanTypeDeclarationSQL(array $column) + public function getBooleanTypeDeclarationSQL(array $column): string { return 'NUMBER(1)'; } @@ -294,7 +200,7 @@ public function getBooleanTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getIntegerTypeDeclarationSQL(array $column) + public function getIntegerTypeDeclarationSQL(array $column): string { return 'NUMBER(10)'; } @@ -302,7 +208,7 @@ public function getIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBigIntTypeDeclarationSQL(array $column) + public function getBigIntTypeDeclarationSQL(array $column): string { return 'NUMBER(20)'; } @@ -310,7 +216,7 @@ public function getBigIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getSmallIntTypeDeclarationSQL(array $column) + public function getSmallIntTypeDeclarationSQL(array $column): string { return 'NUMBER(5)'; } @@ -318,7 +224,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getDateTimeTypeDeclarationSQL(array $column): string { return 'TIMESTAMP(0)'; } @@ -326,7 +232,7 @@ public function getDateTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTzTypeDeclarationSQL(array $column) + public function getDateTimeTzTypeDeclarationSQL(array $column): string { return 'TIMESTAMP(0) WITH TIME ZONE'; } @@ -334,7 +240,7 @@ public function getDateTimeTzTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTypeDeclarationSQL(array $column) + public function getDateTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -342,7 +248,7 @@ public function getDateTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getTimeTypeDeclarationSQL(array $column) + public function getTimeTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -350,116 +256,78 @@ public function getTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string { return ''; } - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default string column length on Oracle is deprecated' - . ', specify the length explicitly.', - ); + if ($length === null) { + throw ColumnLengthRequired::new($this, 'VARCHAR2'); } - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(2000)') - : ($length > 0 ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); + return sprintf('VARCHAR2(%d)', $length); } - /** - * {@inheritDoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) + protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length on Oracle is deprecated' - . ', specify the length explicitly.', - ); + if ($length === null) { + throw ColumnLengthRequired::new($this, 'RAW'); } - return 'RAW(' . ($length > 0 ? $length : $this->getBinaryMaxLength()) . ')'; + return sprintf('RAW(%d)', $length); } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getBinaryMaxLength() + protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'OraclePlatform::getBinaryMaxLength() is deprecated.', - ); - - return 2000; + return $this->getBinaryTypeDeclarationSQLSnippet($length); } /** * {@inheritDoc} */ - public function getClobTypeDeclarationSQL(array $column) + public function getClobTypeDeclarationSQL(array $column): string { return 'CLOB'; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListDatabasesSQL() + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListDatabasesSQL(): string { return 'SELECT username FROM all_users'; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListSequencesSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListSequencesSQL(string $database): string { - $database = $this->normalizeIdentifier($database); - $database = $this->quoteStringLiteral($database->getName()); - - return 'SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ' . - 'WHERE SEQUENCE_OWNER = ' . $database; + return 'SELECT SEQUENCE_NAME, MIN_VALUE, INCREMENT_BY FROM SYS.ALL_SEQUENCES WHERE SEQUENCE_OWNER = ' + . $this->quoteStringLiteral( + $this->normalizeIdentifier($database)->getName(), + ); } /** * {@inheritDoc} */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { $indexes = $options['indexes'] ?? []; $options['indexes'] = []; $sql = parent::_getCreateTableSQL($name, $columns, $options); - foreach ($columns as $columnName => $column) { + foreach ($columns as $column) { if (isset($column['sequence'])) { $sql[] = $this->getCreateSequenceSQL($column['sequence']); } if ( - ! isset($column['autoincrement']) || ! $column['autoincrement'] && - (! isset($column['autoinc']) || ! $column['autoinc']) + empty($column['autoincrement']) ) { continue; } - $sql = array_merge($sql, $this->getCreateAutoincrementSql($columnName, $name)); + $sql = array_merge($sql, $this->getCreateAutoincrementSql($column['name'], $name)); } foreach ($indexes as $index) { @@ -469,78 +337,14 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] return $sql; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - * - * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html - */ - public function getListTableIndexesSQL($table, $database = null) - { - $table = $this->normalizeIdentifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return "SELECT uind_col.index_name AS name, - ( - SELECT uind.index_type - FROM user_indexes uind - WHERE uind.index_name = uind_col.index_name - ) AS type, - decode( - ( - SELECT uind.uniqueness - FROM user_indexes uind - WHERE uind.index_name = uind_col.index_name - ), - 'NONUNIQUE', - 0, - 'UNIQUE', - 1 - ) AS is_unique, - uind_col.column_name AS column_name, - uind_col.column_position AS column_pos, - ( - SELECT ucon.constraint_type - FROM user_constraints ucon - WHERE ucon.index_name = uind_col.index_name - AND ucon.table_name = uind_col.table_name - ) AS is_primary - FROM user_ind_columns uind_col - WHERE uind_col.table_name = " . $table . ' - ORDER BY uind_col.column_position ASC'; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return 'SELECT * FROM sys.user_tables'; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string { return 'SELECT view_name, text FROM sys.user_views'; } - /** - * @internal The method should be only used from within the OraclePlatform class hierarchy. - * - * @param string $name - * @param string $table - * @param int $start - * - * @return string[] - */ - public function getCreateAutoincrementSql($name, $table, $start = 1) + /** @return array */ + protected function getCreateAutoincrementSql(string $name, string $table, int $start = 1): array { $tableIdentifier = $this->normalizeIdentifier($table); $quotedTableName = $tableIdentifier->getQuotedName($this); @@ -564,13 +368,12 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) WHERE TABLE_NAME = '" . $unquotedTableName . "' AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 OR constraints_Count = '' THEN - EXECUTE IMMEDIATE '" . $this->getCreateConstraintSQL($idx, $quotedTableName) . "'; + EXECUTE IMMEDIATE '" . $this->getCreateIndexSQL($idx, $quotedTableName) . "'; END IF; END;"; $sequenceName = $this->getIdentitySequenceName( $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, - $nameIdentifier->isQuoted() ? $quotedName : $unquotedName, ); $sequence = new Sequence($sequenceName, $start); $sql[] = $this->getCreateSequenceSQL($sequence); @@ -609,13 +412,12 @@ public function getCreateAutoincrementSql($name, $table, $start = 1) * * @return string[] */ - public function getDropAutoincrementSql($table) + public function getDropAutoincrementSql(string $table): array { $table = $this->normalizeIdentifier($table); $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); $identitySequenceName = $this->getIdentitySequenceName( $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), - '', ); return [ @@ -633,7 +435,7 @@ public function getDropAutoincrementSql($table) * * @param string $name The identifier to normalize. */ - private function normalizeIdentifier($name): Identifier + private function normalizeIdentifier(string $name): Identifier { $identifier = new Identifier($name); @@ -671,139 +473,13 @@ private function getAutoincrementIdentifierName(Identifier $table): string : $identifierName; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableForeignKeysSQL($table) - { - $table = $this->normalizeIdentifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return "SELECT alc.constraint_name, - alc.DELETE_RULE, - cols.column_name \"local_column\", - cols.position, - ( - SELECT r_cols.table_name - FROM user_cons_columns r_cols - WHERE alc.r_constraint_name = r_cols.constraint_name - AND r_cols.position = cols.position - ) AS \"references_table\", - ( - SELECT r_cols.column_name - FROM user_cons_columns r_cols - WHERE alc.r_constraint_name = r_cols.constraint_name - AND r_cols.position = cols.position - ) AS \"foreign_column\" - FROM user_cons_columns cols - JOIN user_constraints alc - ON alc.constraint_name = cols.constraint_name - AND alc.constraint_type = 'R' - AND alc.table_name = " . $table . ' - ORDER BY cols.constraint_name ASC, cols.position ASC'; - } - - /** - * @deprecated - * - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - $table = $this->normalizeIdentifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return 'SELECT * FROM user_constraints WHERE table_name = ' . $table; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) + public function getDropForeignKeySQL(string $foreignKey, string $table): string { - $table = $this->normalizeIdentifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - $tabColumnsTableName = 'user_tab_columns'; - $colCommentsTableName = 'user_col_comments'; - $tabColumnsOwnerCondition = ''; - $colCommentsOwnerCondition = ''; - - if ($database !== null && $database !== '/') { - $database = $this->normalizeIdentifier($database); - $database = $this->quoteStringLiteral($database->getName()); - $tabColumnsTableName = 'all_tab_columns'; - $colCommentsTableName = 'all_col_comments'; - $tabColumnsOwnerCondition = ' AND c.owner = ' . $database; - $colCommentsOwnerCondition = ' AND d.OWNER = c.OWNER'; - } - - return sprintf( - <<<'SQL' -SELECT c.*, - ( - SELECT d.comments - FROM %s d - WHERE d.TABLE_NAME = c.TABLE_NAME%s - AND d.COLUMN_NAME = c.COLUMN_NAME - ) AS comments -FROM %s c -WHERE c.table_name = %s%s -ORDER BY c.column_id -SQL - , - $colCommentsTableName, - $colCommentsOwnerCondition, - $tabColumnsTableName, - $table, - $tabColumnsOwnerCondition, - ); + return $this->getDropConstraintSQL($foreignKey, $table); } - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - if ($foreignKey instanceof ForeignKeyConstraint) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' - . ' Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $foreignKey = new Identifier($foreignKey); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - } else { - $table = new Identifier($table); - } - - $foreignKey = $foreignKey->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string { $referentialAction = ''; @@ -818,44 +494,26 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey return ''; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getForeignKeyReferentialActionSQL($action) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getForeignKeyReferentialActionSQL(string $action): string { $action = strtoupper($action); - switch ($action) { - case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION. - case 'NO ACTION': - // NO ACTION cannot be declared explicitly, - // therefore returning empty string to indicate to OMIT the referential clause. - return ''; - - case 'CASCADE': - case 'SET NULL': - return $action; - - default: - // SET DEFAULT is not supported, throw exception instead. - throw new InvalidArgumentException('Invalid foreign key action: ' . $action); - } + return match ($action) { + 'RESTRICT', + 'NO ACTION' => '', + 'CASCADE', + 'SET NULL' => $action, + default => throw new InvalidArgumentException(sprintf('Invalid foreign key action "%s".', $action)), + }; } - /** - * {@inheritDoc} - */ - public function getCreateDatabaseSQL($name) + public function getCreateDatabaseSQL(string $name): string { return 'CREATE USER ' . $name; } - /** - * {@inheritDoc} - */ - public function getDropDatabaseSQL($name) + public function getDropDatabaseSQL(string $name): string { return 'DROP USER ' . $name . ' CASCADE'; } @@ -863,25 +521,21 @@ public function getDropDatabaseSQL($name) /** * {@inheritDoc} */ - public function getAlterTableSQL(TableDiff $diff) + public function getAlterTableSQL(TableDiff $diff): array { $sql = []; $commentsSQL = []; $columnSql = []; - $fields = []; + $addColumnSQL = []; - $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); + $tableNameSQL = $diff->getOldTable()->getQuotedName($this); foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); - $comment = $this->getColumnComment($column); + $addColumnSQL[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); + $comment = $column->getComment(); - if ($comment === null || $comment === '') { + if ($comment === '') { continue; } @@ -892,113 +546,68 @@ public function getAlterTableSQL(TableDiff $diff) ); } - if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $fields) . ')'; + if (count($addColumnSQL) > 0) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $addColumnSQL) . ')'; } - $fields = []; + $modifyColumnSQL = []; foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - $newColumn = $columnDiff->getNewColumn(); + $oldColumn = $columnDiff->getOldColumn(); - // Do not generate column alteration clause if type is binary and only fixed property has changed. - // Oracle only supports binary type columns with variable length. - // Avoids unnecessary table alteration statements. - if ( - $newColumn->getType() instanceof BinaryType && - $columnDiff->hasFixedChanged() && - count($columnDiff->changedProperties) === 1 - ) { - continue; - } + $newColumnProperties = $newColumn->toArray(); + $oldColumnProperties = $oldColumn->toArray(); - $columnHasChangedComment = $columnDiff->hasCommentChanged(); - - /** - * Do not add query part if only comment has changed - */ - if (! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { - $newColumnProperties = $newColumn->toArray(); + $oldSQL = $this->getColumnDeclarationSQL('', $oldColumnProperties); + $newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties); + if ($newSQL !== $oldSQL) { if (! $columnDiff->hasNotNullChanged()) { unset($newColumnProperties['notnull']); + $newSQL = $this->getColumnDeclarationSQL('', $newColumnProperties); } - $fields[] = $newColumn->getQuotedName($this) . $this->getColumnDeclarationSQL('', $newColumnProperties); + $modifyColumnSQL[] = $newColumn->getQuotedName($this) . $newSQL; } - if (! $columnHasChangedComment) { + if (! $columnDiff->hasCommentChanged()) { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $newColumn->getQuotedName($this), - $this->getColumnComment($newColumn), + $newColumn->getComment(), ); } - if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $fields) . ')'; + if (count($modifyColumnSQL) > 0) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $modifyColumnSQL) . ')'; } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - $oldColumnName = new Identifier($oldColumnName); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } - $fields = []; + $dropColumnSQL = []; foreach ($diff->getDroppedColumns() as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $fields[] = $column->getQuotedName($this); + $dropColumnSQL[] = $column->getQuotedName($this); } - if (count($fields) > 0) { - $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $fields) . ')'; - } - - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $sql[] = sprintf( - 'ALTER TABLE %s RENAME TO %s', - $tableNameSQL, - $newName->getQuotedName($this), - ); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff), - ); + if (count($dropColumnSQL) > 0) { + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $dropColumnSQL) . ')'; } - return array_merge($sql, $tableSql, $columnSql); + return array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $commentsSQL, + $this->getPostAlterTableIndexForeignKeySQL($diff), + $columnSql, + ); } /** @@ -1006,10 +615,10 @@ public function getAlterTableSQL(TableDiff $diff) * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getColumnDeclarationSQL($name, array $column) + public function getColumnDeclarationSQL(string $name, array $column): string { if (isset($column['columnDefinition'])) { - $columnDef = $this->getCustomTypeDeclarationSQL($column); + $declaration = $column['columnDefinition']; } else { $default = $this->getDefaultValueDeclarationSQL($column); @@ -1019,43 +628,19 @@ public function getColumnDeclarationSQL($name, array $column) $notnull = $column['notnull'] ? ' NOT NULL' : ' NULL'; } - if (! empty($column['unique'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', - ); - - $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); - } else { - $unique = ''; - } - - if (! empty($column['check'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "check" column property is deprecated.', - ); - - $check = ' ' . $column['check']; - } else { - $check = ''; - } - - $typeDecl = $column['type']->getSQLDeclaration($column, $this); - $columnDef = $typeDecl . $default . $notnull . $unique . $check; + $typeDecl = $column['type']->getSQLDeclaration($column, $this); + $declaration = $typeDecl . $default . $notnull; } - return $name . ' ' . $columnDef; + return $name . ' ' . $declaration; } /** * {@inheritDoc} */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array { - if (strpos($tableName, '.') !== false) { + if (str_contains($tableName, '.')) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } @@ -1063,29 +648,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function usesSequenceEmulatedIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the OraclePlatform class hierarchy. - */ - public function getIdentitySequenceName($tableName, $columnName) + protected function getIdentitySequenceName(string $tableName): string { $table = new Identifier($tableName); @@ -1101,144 +664,73 @@ public function getIdentitySequenceName($tableName, $columnName) return $identitySequenceIdentifier->getQuotedName($this); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsCommentOnStatement() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsCommentOnStatement(): bool { return true; } - /** - * {@inheritDoc} - */ - public function getName() + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4749', - 'OraclePlatform::getName() is deprecated. Identify platforms by their class.', - ); - - return 'oracle'; - } - - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) - { - if ($limit === null && $offset <= 0) { - return $query; + if ($offset > 0) { + $query .= sprintf(' OFFSET %d ROWS', $offset); } - if (preg_match('/^\s*SELECT/i', $query) === 1) { - if (preg_match('/\sFROM\s/i', $query) === 0) { - $query .= ' FROM dual'; - } - - $columns = ['a.*']; - - if ($offset > 0) { - $columns[] = 'ROWNUM AS doctrine_rownum'; - } - - $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query); - - if ($limit !== null) { - $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit); - } - - if ($offset > 0) { - $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1); - } + if ($limit !== null) { + $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); } return $query; } - /** - * {@inheritDoc} - */ - public function getCreateTemporaryTableSnippetSQL() + public function getCreateTemporaryTableSnippetSQL(): string { return 'CREATE GLOBAL TEMPORARY TABLE'; } - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() + public function getDateTimeTzFormatString(): string { return 'Y-m-d H:i:sP'; } - /** - * {@inheritDoc} - */ - public function getDateFormatString() + public function getDateFormatString(): string { return 'Y-m-d 00:00:00'; } - /** - * {@inheritDoc} - */ - public function getTimeFormatString() + public function getTimeFormatString(): string { return '1900-01-01 H:i:s'; } - /** - * {@inheritDoc} - */ - public function getMaxIdentifierLength() + public function getMaxIdentifierLength(): int { - return 30; + return 128; } - /** - * {@inheritDoc} - */ - public function supportsSequences() + public function supportsSequences(): bool { return true; } - /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() + public function supportsReleaseSavepoints(): bool { return false; } - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); } - /** - * {@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); } - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings(): void { $this->doctrineTypeMapping = [ 'binary_double' => Types::FLOAT, @@ -1267,63 +759,24 @@ protected function initializeDoctrineTypeMappings() ]; } - /** - * {@inheritDoc} - */ - public function releaseSavePoint($savepoint) + public function releaseSavePoint(string $savepoint): string { return ''; } - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() + protected function createReservedKeywordsList(): KeywordList { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'OraclePlatform::getReservedKeywordsClass() is deprecated,' - . ' use OraclePlatform::createReservedKeywordsList() instead.', - ); - - return Keywords\OracleKeywords::class; + return new OracleKeywords(); } /** * {@inheritDoc} */ - public function getBlobTypeDeclarationSQL(array $column) + public function getBlobTypeDeclarationSQL(array $column): string { return 'BLOB'; } - /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ - public function getListTableCommentsSQL(string $table, ?string $database = null): string - { - $tableCommentsName = 'user_tab_comments'; - $ownerCondition = ''; - - if ($database !== null && $database !== '/') { - $tableCommentsName = 'all_tab_comments'; - $ownerCondition = ' AND owner = ' . $this->quoteStringLiteral( - $this->normalizeIdentifier($database)->getName(), - ); - } - - return sprintf( - <<<'SQL' -SELECT comments FROM %s WHERE table_name = %s%s -SQL - , - $tableCommentsName, - $this->quoteStringLiteral($this->normalizeIdentifier($table)->getName()), - $ownerCondition, - ); - } - public function createSchemaManager(Connection $connection): OracleSchemaManager { return new OracleSchemaManager($connection, $this); diff --git a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php b/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php deleted file mode 100644 index 6ee1f90f2..000000000 --- a/doctrine/dbal/src/Platforms/PostgreSQL100Platform.php +++ /dev/null @@ -1,36 +0,0 @@ -useBooleanTrueFalseStrings = (bool) $flag; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) - { - if ($length === null) { - return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; - } - - return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; - } - - /** - * {@inheritDoc} - * - * @deprecated Generate dates within the application. */ - public function getNowExpression() + public function setUseBooleanTrueFalseStrings(bool $flag): void { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4753', - 'PostgreSQLPlatform::getNowExpression() is deprecated. Generate dates within the application.', - ); - - return 'LOCALTIMESTAMP(0)'; + $this->useBooleanTrueFalseStrings = $flag; } - /** - * {@inheritDoc} - */ - public function getRegexpExpression() + public function getRegexpExpression(): string { return 'SIMILAR TO'; } - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) + public function getLocateExpression(string $string, string $substring, ?string $start = null): string { - if ($startPos !== false) { - $str = $this->getSubstringExpression($str, $startPos); + if ($start !== null) { + $string = $this->getSubstringExpression($string, $start); - return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' - . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . $startPos . ' - 1) END'; + return 'CASE WHEN (POSITION(' . $substring . ' IN ' . $string . ') = 0) THEN 0' + . ' ELSE (POSITION(' . $substring . ' IN ' . $string . ') + ' . $start . ' - 1) END'; } - return 'POSITION(' . $substr . ' IN ' . $str . ')'; + return sprintf('POSITION(%s IN %s)', $substring, $string); } - /** - * {@inheritDoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { + protected function getDateArithmeticIntervalExpression( + string $date, + string $operator, + string $interval, + DateIntervalUnit $unit, + ): string { if ($unit === DateIntervalUnit::QUARTER) { - $interval = $this->multiplyInterval((string) $interval, 3); + $interval = $this->multiplyInterval($interval, 3); $unit = DateIntervalUnit::MONTH; } - return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; + return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit->value . "')::interval)"; } - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) + public function getDateDiffExpression(string $date1, string $date2): string { return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; } @@ -156,159 +111,41 @@ public function getCurrentDatabaseExpression(): string return 'CURRENT_DATABASE()'; } - /** - * {@inheritDoc} - */ - public function supportsSequences() + public function supportsSequences(): bool { return true; } - /** - * {@inheritDoc} - */ - public function supportsSchemas() + public function supportsSchemas(): bool { return true; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getDefaultSchemaName() + public function supportsIdentityColumns(): bool { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return 'public'; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsPartialIndexes() - { - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function usesSequenceEmulatedIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - return true; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getIdentitySequenceName($tableName, $columnName) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return $tableName . '_' . $columnName . '_seq'; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsCommentOnStatement() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsPartialIndexes(): bool { return true; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function hasNativeGuidType() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsCommentOnStatement(): bool { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - return true; } - public function createSelectSQLBuilder(): SelectSQLBuilder - { - return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', null); - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListDatabasesSQL() + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListDatabasesSQL(): string { return 'SELECT datname FROM pg_database'; } - /** - * {@inheritDoc} - * - * @deprecated Use {@see PostgreSQLSchemaManager::listSchemaNames()} instead. - */ - public function getListNamespacesSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'PostgreSQLPlatform::getListNamespacesSQL() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', - ); - - return "SELECT schema_name AS nspname - FROM information_schema.schemata - WHERE schema_name NOT LIKE 'pg\_%' - AND schema_name != 'information_schema'"; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListSequencesSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListSequencesSQL(string $database): string { return 'SELECT sequence_name AS relname, sequence_schema AS schemaname, @@ -320,29 +157,8 @@ public function getListSequencesSQL($database) AND sequence_schema != 'information_schema'"; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return "SELECT quote_ident(table_name) AS table_name, - table_schema AS schema_name - FROM information_schema.tables - WHERE table_schema NOT LIKE 'pg\_%' - AND table_schema != 'information_schema' - AND table_name != 'geometry_columns' - AND table_name != 'spatial_ref_sys' - AND table_type != 'VIEW'"; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string { return 'SELECT quote_ident(table_name) AS viewname, table_schema AS schemaname, @@ -351,150 +167,8 @@ public function getListViewsSQL($database) WHERE view_definition IS NOT NULL'; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef - FROM pg_catalog.pg_constraint r - WHERE r.conrelid = - ( - SELECT c.oid - FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n - WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace - ) - AND r.contype = 'f'"; - } - - /** - * @deprecated - * - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return sprintf( - <<<'SQL' -SELECT - quote_ident(relname) as relname -FROM - pg_class -WHERE oid IN ( - SELECT indexrelid - FROM pg_index, pg_class - WHERE pg_class.relname = %s - AND pg_class.oid = pg_index.indrelid - AND (indisunique = 't' OR indisprimary = 't') - ) -SQL - , - $table, - ); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - * - * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html - */ - public function getListTableIndexesSQL($table, $database = null) - { - return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, - pg_index.indkey, pg_index.indrelid, - pg_get_expr(indpred, indrelid) AS where - FROM pg_class, pg_index - WHERE oid IN ( - SELECT indexrelid - FROM pg_index si, pg_class sc, pg_namespace sn - WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' - AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid - ) AND pg_index.indexrelid = oid'; - } - - /** - * @param string $table - * @param string $classAlias - * @param string $namespaceAlias - */ - private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n'): string - { - $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - } else { - $schema = 'ANY(current_schemas(false))'; - } - - $table = new Identifier($table); - $table = $this->quoteStringLiteral($table->getName()); - - return $whereClause . sprintf( - '%s.relname = %s AND %s.nspname = %s', - $classAlias, - $table, - $namespaceAlias, - $schema, - ); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT - a.attnum, - 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, - a.attnotnull AS isnotnull, - (SELECT 't' - FROM pg_index - WHERE c.oid = pg_index.indrelid - AND pg_index.indkey[0] = a.attnum - AND pg_index.indisprimary = 't' - ) AS pri, - (SELECT pg_get_expr(adbin, adrelid) - FROM pg_attrdef - WHERE c.oid = pg_attrdef.adrelid - AND pg_attrdef.adnum=a.attnum - ) AS default, - (SELECT pg_description.description - FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid - ) AS comment - FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n - WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' - AND a.attnum > 0 - AND a.attrelid = c.oid - AND a.atttypid = t.oid - AND n.oid = c.relnamespace - ORDER BY a.attnum'; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string { $query = ''; @@ -511,8 +185,7 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey } if ( - ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) - || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) + $foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false ) { $query .= ' INITIALLY DEFERRED'; } else { @@ -525,21 +198,17 @@ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey /** * {@inheritDoc} */ - public function getAlterTableSQL(TableDiff $diff) + public function getAlterTableSQL(TableDiff $diff): array { $sql = []; $commentsSQL = []; $columnSql = []; - $table = $diff->getOldTable() ?? $diff->getName($this); + $table = $diff->getOldTable(); $tableNameSQL = $table->getQuotedName($this); foreach ($diff->getAddedColumns() as $addedColumn) { - if ($this->onSchemaAlterTableAddColumn($addedColumn, $diff, $columnSql)) { - continue; - } - $query = 'ADD ' . $this->getColumnDeclarationSQL( $addedColumn->getQuotedName($this), $addedColumn->toArray(), @@ -547,9 +216,9 @@ public function getAlterTableSQL(TableDiff $diff) $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; - $comment = $this->getColumnComment($addedColumn); + $comment = $addedColumn->getComment(); - if ($comment === null || $comment === '') { + if ($comment === '') { continue; } @@ -561,24 +230,12 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getDroppedColumns() as $droppedColumn) { - if ($this->onSchemaAlterTableRemoveColumn($droppedColumn, $diff, $columnSql)) { - continue; - } - $query = 'DROP ' . $droppedColumn->getQuotedName($this); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - if ($this->isUnchangedBinaryColumn($columnDiff)) { - continue; - } - - $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); + $oldColumn = $columnDiff->getOldColumn(); $newColumn = $columnDiff->getNewColumn(); $oldColumnName = $oldColumn->getQuotedName($this); @@ -616,31 +273,18 @@ public function getAlterTableSQL(TableDiff $diff) if ($columnDiff->hasAutoIncrementChanged()) { if ($newColumn->getAutoincrement()) { - // add autoincrement - $seqName = $this->getIdentitySequenceName( - $table->getName(), - $oldColumnName, - ); - - $sql[] = 'CREATE SEQUENCE ' . $seqName; - $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' - . $tableNameSQL . '))'; - $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; + $query = 'ADD GENERATED BY DEFAULT AS IDENTITY'; } else { - // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have - $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; + $query = 'DROP IDENTITY'; } - $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; + $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ALTER ' . $oldColumnName . ' ' . $query; } - $oldComment = $this->getOldColumnComment($columnDiff); - $newComment = $this->getColumnComment($newColumn); + $newComment = $newColumn->getComment(); + $oldComment = $columnDiff->getOldColumn()->getComment(); - if ( - $columnDiff->hasCommentChanged() - || ($columnDiff->getOldColumn() !== null && $oldComment !== $newComment) - ) { + if ($columnDiff->hasCommentChanged() || $oldComment !== $newComment) { $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $newColumn->getQuotedName($this), @@ -658,90 +302,27 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - $oldColumnName = new Identifier($oldColumnName); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } - $tableSql = []; - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - $sql = array_merge($sql, $commentsSQL); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $sql[] = sprintf( - 'ALTER TABLE %s RENAME TO %s', - $tableNameSQL, - $newName->getQuotedName($this), - ); - } - - $sql = array_merge( - $this->getPreAlterTableIndexForeignKeySQL($diff), - $sql, - $this->getPostAlterTableIndexForeignKeySQL($diff), - ); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Checks whether a given column diff is a logically unchanged binary type column. - * - * Used to determine whether a column alteration for a binary type column can be skipped. - * Doctrine's {@see BinaryType} and {@see BlobType} are mapped to the same database column type on this platform - * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator - * might detect differences for binary type columns which do not have to be propagated - * to database as there actually is no difference at database level. - */ - private function isUnchangedBinaryColumn(ColumnDiff $columnDiff): bool - { - $newColumnType = $columnDiff->getNewColumn()->getType(); - - if (! $newColumnType instanceof BinaryType && ! $newColumnType instanceof BlobType) { - return false; - } - - $oldColumn = $columnDiff->getOldColumn() instanceof Column ? $columnDiff->getOldColumn() : null; - - if ($oldColumn !== null) { - $oldColumnType = $oldColumn->getType(); - - if (! $oldColumnType instanceof BinaryType && ! $oldColumnType instanceof BlobType) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; - } - - if ($columnDiff->hasTypeChanged()) { - return false; - } - - return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; + return array_merge( + $this->getPreAlterTableIndexForeignKeySQL($diff), + $sql, + $commentsSQL, + $this->getPostAlterTableIndexForeignKeySQL($diff), + $columnSql, + ); } /** * {@inheritDoc} */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array { - if (strpos($tableName, '.') !== false) { + if (str_contains($tableName, '.')) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } @@ -749,29 +330,7 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getCommentOnColumnSQL($tableName, $columnName, $comment) - { - $tableName = new Identifier($tableName); - $columnName = new Identifier($columnName); - $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); - - return sprintf( - 'COMMENT ON COLUMN %s.%s IS %s', - $tableName->getQuotedName($this), - $columnName->getQuotedName($this), - $comment, - ); - } - - /** - * {@inheritDoc} - */ - public function getCreateSequenceSQL(Sequence $sequence) + public function getCreateSequenceSQL(Sequence $sequence): string { return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() . @@ -780,10 +339,7 @@ public function getCreateSequenceSQL(Sequence $sequence) $this->getSequenceCacheSQL($sequence); } - /** - * {@inheritDoc} - */ - public function getAlterSequenceSQL(Sequence $sequence) + public function getAlterSequenceSQL(Sequence $sequence): string { return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() . @@ -802,56 +358,31 @@ private function getSequenceCacheSQL(Sequence $sequence): string return ''; } - /** - * {@inheritDoc} - */ - public function getDropSequenceSQL($sequence) + public function getDropSequenceSQL(string $name): string { - return parent::getDropSequenceSQL($sequence) . ' CASCADE'; + return parent::getDropSequenceSQL($name) . ' CASCADE'; } - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) + public function getDropForeignKeySQL(string $foreignKey, string $table): string { return $this->getDropConstraintSQL($foreignKey, $table); } - /** - * {@inheritDoc} - */ - public function getDropIndexSQL($index, $table = null) + public function getDropIndexSQL(string $name, string $table): string { - if ($index instanceof Index && $index->isPrimary() && $table !== null) { - $constraintName = $index->getName() === 'primary' ? $this->tableName($table) . '_pkey' : $index->getName(); - - return $this->getDropConstraintSQL($constraintName, $table); - } - - if ($index === '"primary"' && $table !== null) { - $constraintName = $this->tableName($table) . '_pkey'; + if ($name === '"primary"') { + $constraintName = $table . '_pkey'; return $this->getDropConstraintSQL($constraintName, $table); } - return parent::getDropIndexSQL($index, $table); - } - - /** - * @param Table|string|null $table - * - * @return string - */ - private function tableName($table) - { - return $table instanceof Table ? $table->getName() : (string) $table; + return parent::getDropIndexSQL($name, $table); } /** * {@inheritDoc} */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { $queryFields = $this->getColumnDeclarationListSQL($columns); @@ -874,7 +405,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] if (isset($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $uniqueConstraint) { - $sql[] = $this->getCreateConstraintSQL($uniqueConstraint, $name); + $sql[] = $this->getCreateUniqueConstraintSQL($uniqueConstraint, $name); } } @@ -897,11 +428,9 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] * @param mixed $value The value to convert. * @param callable $callback The callback function to use for converting the real boolean value. * - * @return mixed - * * @throws UnexpectedValueException */ - private function convertSingleBooleanValue($value, $callback) + private function convertSingleBooleanValue(mixed $value, callable $callback): mixed { if ($value === null) { return $callback(null); @@ -926,7 +455,10 @@ private function convertSingleBooleanValue($value, $callback) return $callback(true); } - throw new UnexpectedValueException(sprintf("Unrecognized boolean literal '%s'", $value)); + throw new UnexpectedValueException(sprintf( + 'Unrecognized boolean literal, %s given.', + $value, + )); } /** @@ -938,10 +470,8 @@ private function convertSingleBooleanValue($value, $callback) * * @param mixed $item The value(s) to convert. * @param callable $callback The callback function to use for converting the real boolean value(s). - * - * @return mixed */ - private function doConvertBooleans($item, $callback) + private function doConvertBooleans(mixed $item, callable $callback): mixed { if (is_array($item)) { foreach ($item as $key => $value) { @@ -959,7 +489,7 @@ private function doConvertBooleans($item, $callback) * * Postgres wants boolean values converted to the strings 'true'/'false'. */ - public function convertBooleans($item) + public function convertBooleans(mixed $item): mixed { if (! $this->useBooleanTrueFalseStrings) { return parent::convertBooleans($item); @@ -968,7 +498,7 @@ public function convertBooleans($item) return $this->doConvertBooleans( $item, /** @param mixed $value */ - static function ($value) { + static function ($value): string { if ($value === null) { return 'NULL'; } @@ -978,10 +508,7 @@ static function ($value) { ); } - /** - * {@inheritDoc} - */ - public function convertBooleansToDatabaseValue($item) + public function convertBooleansToDatabaseValue(mixed $item): mixed { if (! $this->useBooleanTrueFalseStrings) { return parent::convertBooleansToDatabaseValue($item); @@ -997,35 +524,27 @@ static function ($value): ?int { } /** - * {@inheritDoc} - * * @param T $item * * @return (T is null ? null : bool) * * @template T */ - public function convertFromBoolean($item) + public function convertFromBoolean(mixed $item): ?bool { - if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { + if (in_array($item, $this->booleanLiterals['false'], true)) { return false; } return parent::convertFromBoolean($item); } - /** - * {@inheritDoc} - */ - public function getSequenceNextValSQL($sequence) + public function getSequenceNextValSQL(string $sequence): string { return "SELECT NEXTVAL('" . $sequence . "')"; } - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string { return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); @@ -1034,7 +553,7 @@ public function getSetTransactionIsolationSQL($level) /** * {@inheritDoc} */ - public function getBooleanTypeDeclarationSQL(array $column) + public function getBooleanTypeDeclarationSQL(array $column): string { return 'BOOLEAN'; } @@ -1042,43 +561,31 @@ public function getBooleanTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getIntegerTypeDeclarationSQL(array $column) + public function getIntegerTypeDeclarationSQL(array $column): string { - if (! empty($column['autoincrement'])) { - return 'SERIAL'; - } - - return 'INT'; + return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function getBigIntTypeDeclarationSQL(array $column) + public function getBigIntTypeDeclarationSQL(array $column): string { - if (! empty($column['autoincrement'])) { - return 'BIGSERIAL'; - } - - return 'BIGINT'; + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function getSmallIntTypeDeclarationSQL(array $column) + public function getSmallIntTypeDeclarationSQL(array $column): string { - if (! empty($column['autoincrement'])) { - return 'SMALLSERIAL'; - } - - return 'SMALLINT'; + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ - public function getGuidTypeDeclarationSQL(array $column) + public function getGuidTypeDeclarationSQL(array $column): string { return 'UUID'; } @@ -1086,7 +593,7 @@ public function getGuidTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getDateTimeTypeDeclarationSQL(array $column): string { return 'TIMESTAMP(0) WITHOUT TIME ZONE'; } @@ -1094,7 +601,7 @@ public function getDateTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTzTypeDeclarationSQL(array $column) + public function getDateTimeTzTypeDeclarationSQL(array $column): string { return 'TIMESTAMP(0) WITH TIME ZONE'; } @@ -1102,7 +609,7 @@ public function getDateTimeTzTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTypeDeclarationSQL(array $column) + public function getDateTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -1110,7 +617,7 @@ public function getDateTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getTimeTypeDeclarationSQL(array $column) + public function getTimeTypeDeclarationSQL(array $column): string { return 'TIME(0) WITHOUT TIME ZONE'; } @@ -1118,70 +625,55 @@ public function getTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string { + if (! empty($column['autoincrement'])) { + return ' GENERATED BY DEFAULT AS IDENTITY'; + } + return ''; } - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) + protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string { - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); + $sql = 'VARCHAR'; + + if ($length !== null) { + $sql .= sprintf('(%d)', $length); + } + + return $sql; } - /** - * {@inheritDoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) + protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string { return 'BYTEA'; } - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) + protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string { - return 'TEXT'; + return 'BYTEA'; } /** * {@inheritDoc} */ - public function getName() + public function getClobTypeDeclarationSQL(array $column): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4749', - 'PostgreSQLPlatform::getName() is deprecated. Identify platforms by their class.', - ); - - return 'postgresql'; + return 'TEXT'; } - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() + public function getDateTimeTzFormatString(): string { return 'Y-m-d H:i:sO'; } - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + public function getEmptyIdentityInsertSQL(string $quotedTableName, string $quotedIdentifierColumnName): string { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; } - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string { $tableIdentifier = new Identifier($tableName); $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); @@ -1193,18 +685,7 @@ public function getTruncateTableSQL($tableName, $cascade = false) return $sql; } - /** - * {@inheritDoc} - */ - public function getReadLockSQL() - { - return 'FOR SHARE'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings(): void { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, @@ -1251,90 +732,15 @@ protected function initializeDoctrineTypeMappings() ]; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getVarcharMaxLength() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'PostgreSQLPlatform::getVarcharMaxLength() is deprecated.', - ); - - return 65535; - } - - /** - * {@inheritDoc} - */ - public function getBinaryMaxLength() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'PostgreSQLPlatform::getBinaryMaxLength() is deprecated.', - ); - - return 0; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getBinaryDefaultLength() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length is deprecated, specify the length explicitly.', - ); - - return 0; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function hasNativeJsonType() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() + protected function createReservedKeywordsList(): KeywordList { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'PostgreSQLPlatform::getReservedKeywordsClass() is deprecated,' - . ' use PostgreSQLPlatform::createReservedKeywordsList() instead.', - ); - - return Keywords\PostgreSQL94Keywords::class; + return new PostgreSQLKeywords(); } /** * {@inheritDoc} */ - public function getBlobTypeDeclarationSQL(array $column) + public function getBlobTypeDeclarationSQL(array $column): string { return 'BYTEA'; } @@ -1344,7 +750,7 @@ public function getBlobTypeDeclarationSQL(array $column) * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getDefaultValueDeclarationSQL($column) + public function getDefaultValueDeclarationSQL(array $column): string { if (isset($column['autoincrement']) && $column['autoincrement'] === true) { return ''; @@ -1353,12 +759,8 @@ public function getDefaultValueDeclarationSQL($column) return parent::getDefaultValueDeclarationSQL($column); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsColumnCollation() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsColumnCollation(): bool { return true; } @@ -1366,7 +768,7 @@ public function supportsColumnCollation() /** * {@inheritDoc} */ - public function getJsonTypeDeclarationSQL(array $column) + public function getJsonTypeDeclarationSQL(array $column): string { if (! empty($column['jsonb'])) { return 'JSONB'; @@ -1375,33 +777,6 @@ public function getJsonTypeDeclarationSQL(array $column) return 'JSON'; } - private function getOldColumnComment(ColumnDiff $columnDiff): ?string - { - $oldColumn = $columnDiff->getOldColumn(); - - if ($oldColumn !== null) { - return $this->getColumnComment($oldColumn); - } - - return null; - } - - /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ - public function getListTableMetadataSQL(string $table, ?string $schema = null): string - { - if ($schema !== null) { - $table = $schema . '.' . $table; - } - - return sprintf( - <<<'SQL' -SELECT obj_description(%s::regclass) AS table_comment; -SQL - , - $this->quoteStringLiteral($table), - ); - } - public function createSchemaManager(Connection $connection): PostgreSQLSchemaManager { return new PostgreSQLSchemaManager($connection, $this); diff --git a/doctrine/dbal/src/Platforms/SQLServer/Comparator.php b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php index 41ef422bf..aa8d9fb5b 100644 --- a/doctrine/dbal/src/Platforms/SQLServer/Comparator.php +++ b/doctrine/dbal/src/Platforms/SQLServer/Comparator.php @@ -1,5 +1,7 @@ databaseCollation = $databaseCollation; } - public function compareTables(Table $fromTable, Table $toTable): TableDiff + public function compareTables(Table $oldTable, Table $newTable): TableDiff { return parent::compareTables( - $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable), - ); - } - - /** - * {@inheritDoc} - */ - public function diffTable(Table $fromTable, Table $toTable) - { - return parent::diffTable( - $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable), + $this->normalizeColumns($oldTable), + $this->normalizeColumns($newTable), ); } diff --git a/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php b/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php index dff12221c..ea6bb6027 100644 --- a/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php +++ b/doctrine/dbal/src/Platforms/SQLServer/SQL/Builder/SQLServerSelectSQLBuilder.php @@ -14,12 +14,10 @@ final class SQLServerSelectSQLBuilder implements SelectSQLBuilder { - private SQLServerPlatform $platform; - /** @internal The SQL builder should be instantiated only by database platforms. */ - public function __construct(SQLServerPlatform $platform) - { - $this->platform = $platform; + public function __construct( + private readonly SQLServerPlatform $platform, + ) { } public function buildSQL(SelectQuery $query): string diff --git a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php b/doctrine/dbal/src/Platforms/SQLServer2012Platform.php deleted file mode 100644 index a8ba2fa0b..000000000 --- a/doctrine/dbal/src/Platforms/SQLServer2012Platform.php +++ /dev/null @@ -1,13 +0,0 @@ -getConvertExpression('date', 'GETDATE()'); } - /** - * {@inheritDoc} - */ - public function getCurrentTimeSQL() + public function getCurrentTimeSQL(): string { return $this->getConvertExpression('time', 'GETDATE()'); } @@ -78,101 +73,53 @@ public function getCurrentTimeSQL() * @param string $dataType The target native data type. Alias data types cannot be used. * @param string $expression The SQL expression to convert. */ - private function getConvertExpression($dataType, $expression): string + private function getConvertExpression(string $dataType, string $expression): string { return sprintf('CONVERT(%s, %s)', $dataType, $expression); } - /** - * {@inheritDoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { + protected function getDateArithmeticIntervalExpression( + string $date, + string $operator, + string $interval, + DateIntervalUnit $unit, + ): string { $factorClause = ''; if ($operator === '-') { $factorClause = '-1 * '; } - return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; + return 'DATEADD(' . $unit->value . ', ' . $factorClause . $interval . ', ' . $date . ')'; } - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) + public function getDateDiffExpression(string $date1, string $date2): string { return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; } - /** - * {@inheritDoc} - * - * Microsoft SQL Server prefers "autoincrement" identity columns - * since sequences can only be emulated with a table. - * - * @deprecated - */ - public function prefersIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/1519', - 'SQLServerPlatform::prefersIdentityColumns() is deprecated.', - ); - - return true; - } - /** * {@inheritDoc} * * Microsoft SQL Server supports this through AUTO_INCREMENT columns. */ - public function supportsIdentityColumns() + public function supportsIdentityColumns(): bool { return true; } - /** - * {@inheritDoc} - */ - public function supportsReleaseSavepoints() + public function supportsReleaseSavepoints(): bool { return false; } - /** - * {@inheritDoc} - */ - public function supportsSchemas() + public function supportsSchemas(): bool { return true; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getDefaultSchemaName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return 'dbo'; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsColumnCollation() + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsColumnCollation(): bool { return true; } @@ -196,12 +143,8 @@ public function getCreateSequenceSQL(Sequence $sequence): string ' MINVALUE ' . $sequence->getInitialValue(); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListSequencesSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListSequencesSQL(string $database): string { return 'SELECT seq.name, CAST( @@ -213,92 +156,25 @@ public function getListSequencesSQL($database) FROM sys.sequences AS seq'; } - /** - * {@inheritDoc} - */ - public function getSequenceNextValSQL($sequence) + public function getSequenceNextValSQL(string $sequence): string { return 'SELECT NEXT VALUE FOR ' . $sequence; } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function hasNativeGuidType() + public function getDropForeignKeySQL(string $foreignKey, string $table): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; + return $this->getDropConstraintSQL($foreignKey, $table); } - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) + public function getDropIndexSQL(string $name, string $table): string { - if (! $foreignKey instanceof ForeignKeyConstraint) { - $foreignKey = new Identifier($foreignKey); - } - - if (! $table instanceof Table) { - $table = new Identifier($table); - } - - $foreignKey = $foreignKey->getQuotedName($this); - $table = $table->getQuotedName($this); - - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; + return 'DROP INDEX ' . $name . ' ON ' . $table; } /** * {@inheritDoc} */ - public function getDropIndexSQL($index, $table = null) - { - if ($index instanceof Index) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $index = $index->getQuotedName($this); - } elseif (! is_string($index)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', - ); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } elseif (! is_string($table)) { - throw new InvalidArgumentException( - __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', - ); - } - - return 'DROP INDEX ' . $index . ' ON ' . $table; - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array { $defaultConstraintsSql = []; $commentsSql = []; @@ -318,7 +194,7 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] // Build default constraints SQL statements. if (isset($column['default'])) { $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . - ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); + ' ADD' . $this->getDefaultConstraintDeclarationSQL($column); } if (empty($column['comment']) && ! is_numeric($column['comment'])) { @@ -331,8 +207,8 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] $columnListSql = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $constraintName => $definition) { - $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); + foreach ($options['uniqueConstraints'] as $definition) { + $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition); } } @@ -372,31 +248,22 @@ protected function _getCreateTableSQL($name, array $columns, array $options = [] return array_merge($sql, $commentsSql, $defaultConstraintsSql); } - /** - * {@inheritDoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $identifier = $table->getQuotedName($this); - } else { - $identifier = $table; - } - - $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; + public function getCreatePrimaryKeySQL(Index $index, string $table): string + { + $sql = 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY'; if ($index->hasFlag('nonclustered')) { $sql .= ' NONCLUSTERED'; } - return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; + return $sql . ' (' . implode(', ', $index->getQuotedColumns($this)) . ')'; + } + + private function unquoteSingleIdentifier(string $possiblyQuotedName): string + { + return str_starts_with($possiblyQuotedName, '[') && str_ends_with($possiblyQuotedName, ']') + ? substr($possiblyQuotedName, 1, -1) + : $possiblyQuotedName; } /** @@ -410,65 +277,47 @@ public function getCreatePrimaryKeySQL(Index $index, $table) * as column comments are stored in the same property there when * specifying a column's "Description" attribute. * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to create the comment for. - * @param string|null $comment The column's comment. - * - * @return string + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to create the comment for. + * @param string $comment The column's comment. */ - protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) + protected function getCreateColumnCommentSQL(string $tableName, string $columnName, string $comment): string { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); + if (str_contains($tableName, '.')) { + [$schemaName, $tableName] = explode('.', $tableName); } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); + $schemaName = 'dbo'; } return $this->getAddExtendedPropertySQL( 'MS_Description', $comment, 'SCHEMA', - $schemaSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)), 'TABLE', - $tableSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)), 'COLUMN', - $columnName, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)), ); } /** * Returns the SQL snippet for declaring a default constraint. * - * @internal The method should be only used from within the SQLServerPlatform class hierarchy. - * - * @param string $table Name of the table to return the default constraint declaration for. * @param mixed[] $column Column definition. - * - * @return string - * - * @throws InvalidArgumentException */ - public function getDefaultConstraintDeclarationSQL($table, array $column) + protected function getDefaultConstraintDeclarationSQL(array $column): string { if (! isset($column['default'])) { - throw new InvalidArgumentException("Incomplete column definition. 'default' required."); + throw new InvalidArgumentException('Incomplete column definition. "default" required.'); } $columnName = new Identifier($column['name']); - return ' CONSTRAINT ' . - $this->generateDefaultConstraintName($table, $column['name']) . - $this->getDefaultValueDeclarationSQL($column) . - ' FOR ' . $columnName->getQuotedName($this); + return $this->getDefaultValueDeclarationSQL($column) . ' FOR ' . $columnName->getQuotedName($this); } - /** - * {@inheritDoc} - */ - public function getCreateIndexSQL(Index $index, $table) + public function getCreateIndexSQL(Index $index, string $table): string { $constraint = parent::getCreateIndexSQL($index, $table); @@ -479,10 +328,7 @@ public function getCreateIndexSQL(Index $index, $table) return $constraint; } - /** - * {@inheritDoc} - */ - protected function getCreateIndexSQLFlags(Index $index) + protected function getCreateIndexSQLFlags(Index $index): string { $type = ''; if ($index->isUnique()) { @@ -500,10 +346,8 @@ protected function getCreateIndexSQLFlags(Index $index) /** * Extend unique key constraint with required filters - * - * @param string $sql */ - private function _appendUniqueConstraintDefinition($sql, Index $index): string + private function _appendUniqueConstraintDefinition(string $sql, Index $index): string { $fields = []; @@ -517,38 +361,31 @@ private function _appendUniqueConstraintDefinition($sql, Index $index): string /** * {@inheritDoc} */ - public function getAlterTableSQL(TableDiff $diff) + public function getAlterTableSQL(TableDiff $diff): array { $queryParts = []; $sql = []; $columnSql = []; $commentsSql = []; - $table = $diff->getOldTable() ?? $diff->getName($this); + $table = $diff->getOldTable(); $tableName = $table->getName(); foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - $columnProperties = $column->toArray(); $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); if (isset($columnProperties['default'])) { - $addColumnSql .= ' CONSTRAINT ' . $this->generateDefaultConstraintName( - $tableName, - $column->getQuotedName($this), - ) . $this->getDefaultValueDeclarationSQL($columnProperties); + $addColumnSql .= $this->getDefaultValueDeclarationSQL($columnProperties); } $queryParts[] = $addColumnSql; - $comment = $this->getColumnComment($column); + $comment = $column->getComment(); - if (empty($comment) && ! is_numeric($comment)) { + if ($comment === '') { continue; } @@ -560,170 +397,106 @@ public function getAlterTableSQL(TableDiff $diff) } foreach ($diff->getDroppedColumns() as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; + if ($column->getDefault() !== null) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($column); } $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - $newColumn = $columnDiff->getNewColumn(); - $newComment = $this->getColumnComment($newColumn); - $hasNewComment = ! empty($newComment) || is_numeric($newComment); - - $oldColumn = $columnDiff->getOldColumn(); - - if ($oldColumn instanceof Column) { - $oldComment = $this->getColumnComment($oldColumn); - $hasOldComment = ! empty($oldComment) || is_numeric($oldComment); - - if ($hasOldComment && $hasNewComment && $oldComment !== $newComment) { - $commentsSql[] = $this->getAlterColumnCommentSQL( - $tableName, - $newColumn->getQuotedName($this), - $newComment, - ); - } elseif ($hasOldComment && ! $hasNewComment) { - $commentsSql[] = $this->getDropColumnCommentSQL( - $tableName, - $newColumn->getQuotedName($this), - ); - } elseif (! $hasOldComment && $hasNewComment) { - $commentsSql[] = $this->getCreateColumnCommentSQL( - $tableName, - $newColumn->getQuotedName($this), - $newComment, - ); - } - } + $newComment = $newColumn->getComment(); + $hasNewComment = $newComment !== ''; - // Do not add query part if only comment has changed. - if ($columnDiff->hasCommentChanged() && count($columnDiff->changedProperties) === 1) { - continue; + $oldColumn = $columnDiff->getOldColumn(); + $oldComment = $oldColumn->getComment(); + $hasOldComment = $oldComment !== ''; + + if ($hasOldComment && $hasNewComment && $newComment !== $oldComment) { + $commentsSql[] = $this->getAlterColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + $newComment, + ); + } elseif ($hasOldComment && ! $hasNewComment) { + $commentsSql[] = $this->getDropColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + ); + } elseif (! $hasOldComment && $hasNewComment) { + $commentsSql[] = $this->getCreateColumnCommentSQL( + $tableName, + $newColumn->getQuotedName($this), + $newComment, + ); } - $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); + $columnNameSQL = $newColumn->getQuotedName($this); - if ($requireDropDefaultConstraint) { - $oldColumn = $columnDiff->getOldColumn(); + $oldDeclarationSQL = $this->getColumnDeclarationSQL($columnNameSQL, $oldColumn->toArray()); + $newDeclarationSQL = $this->getColumnDeclarationSQL($columnNameSQL, $newColumn->toArray()); - if ($oldColumn !== null) { - $oldColumnName = $oldColumn->getName(); - } else { - $oldColumnName = $columnDiff->oldColumnName; - } + $declarationSQLChanged = $newDeclarationSQL !== $oldDeclarationSQL; + $defaultChanged = $columnDiff->hasDefaultChanged(); - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($tableName, $oldColumnName); + if (! $declarationSQLChanged && ! $defaultChanged) { + continue; } - $columnProperties = $newColumn->toArray(); + $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); - $queryParts[] = 'ALTER COLUMN ' . - $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $columnProperties); + if ($requireDropDefaultConstraint) { + $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($oldColumn); + } + + if ($declarationSQLChanged) { + $queryParts[] = 'ALTER COLUMN ' . $newDeclarationSQL; + } if ( - ! isset($columnProperties['default']) - || (! $requireDropDefaultConstraint && ! $columnDiff->hasDefaultChanged()) + $newColumn->getDefault() === null + || (! $requireDropDefaultConstraint && ! $defaultChanged) ) { continue; } - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); + $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); } $tableNameSQL = $table->getQuotedName($this); foreach ($diff->getRenamedColumns() as $oldColumnName => $newColumn) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $newColumn, $diff, $columnSql)) { - continue; - } - $oldColumnName = new Identifier($oldColumnName); - $sql[] = "sp_rename '" . $tableNameSQL . '.' . $oldColumnName->getQuotedName($this) . - "', '" . $newColumn->getQuotedName($this) . "', 'COLUMN'"; - - // Recreate default constraint with new column name if necessary (for future reference). - if ($newColumn->getDefault() === null) { - continue; - } - - $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( - $tableName, + $sql[] = sprintf( + "sp_rename '%s.%s', '%s', 'COLUMN'", + $tableNameSQL, $oldColumnName->getQuotedName($this), + $newColumn->getQuotedName($this), ); - $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); - } - - $tableSql = []; - - if ($this->onSchemaAlterTable($diff, $tableSql)) { - return array_merge($tableSql, $columnSql); } foreach ($queryParts as $query) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } - $sql = array_merge($sql, $commentsSql); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $sql = array_merge($sql, $this->getRenameTableSQL($tableName, $newName->getName())); - } - - $sql = array_merge( + return array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, + $commentsSql, $this->getPostAlterTableIndexForeignKeySQL($diff), + $columnSql, ); - - return array_merge($sql, $tableSql, $columnSql); } - /** - * {@inheritDoc} - */ - public function getRenameTableSQL(string $oldName, string $newName): array - { - return [ - sprintf('sp_rename %s, %s', $this->quoteStringLiteral($oldName), $this->quoteStringLiteral($newName)), - - /* Rename table's default constraints names - * to match the new table name. - * This is necessary to ensure that the default - * constraints can be referenced in future table - * alterations as the table name is encoded in - * default constraints' names. */ - sprintf( - <<<'SQL' - DECLARE @sql NVARCHAR(MAX) = N''; - SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' - + REPLACE(dc.name, '%s', '%s') + ''', ''OBJECT'';' - FROM sys.default_constraints dc - JOIN sys.tables tbl - ON dc.parent_object_id = tbl.object_id - WHERE tbl.name = %s; - EXEC sp_executesql @sql - SQL, - $this->generateIdentifierName($oldName), - $this->generateIdentifierName($newName), - $this->quoteStringLiteral($newName), - ), - ]; + public function getRenameTableSQL(string $oldName, string $newName): string + { + return sprintf( + 'sp_rename %s, %s', + $this->quoteStringLiteral($oldName), + $this->quoteStringLiteral($newName), + ); } /** @@ -732,23 +505,29 @@ public function getRenameTableSQL(string $oldName, string $newName): array * @param string $tableName The name of the table to generate the clause for. * @param Column $column The column to generate the clause for. */ - private function getAlterTableAddDefaultConstraintClause($tableName, Column $column): string + private function getAlterTableAddDefaultConstraintClause(string $tableName, Column $column): string { $columnDef = $column->toArray(); $columnDef['name'] = $column->getQuotedName($this); - return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); + return 'ADD' . $this->getDefaultConstraintDeclarationSQL($columnDef); } /** * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. - * - * @param string $tableName The name of the table to generate the clause for. - * @param string $columnName The name of the column to generate the clause for. */ - private function getAlterTableDropDefaultConstraintClause($tableName, $columnName): string + private function getAlterTableDropDefaultConstraintClause(Column $column): string { - return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); + if (! $column->hasPlatformOption(self::OPTION_DEFAULT_CONSTRAINT_NAME)) { + throw new InvalidArgumentException( + 'Column ' . $column->getName() . ' was not properly introspected as it has a default value' + . ' but does not have the default constraint name.', + ); + } + + return 'DROP CONSTRAINT ' . $this->quoteIdentifier( + $column->getPlatformOption(self::OPTION_DEFAULT_CONSTRAINT_NAME), + ); } /** @@ -761,17 +540,9 @@ private function getAlterTableDropDefaultConstraintClause($tableName, $columnNam */ private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff): bool { - $oldColumn = $columnDiff->getOldColumn(); - - // We can only decide whether to drop an existing default constraint - // if we know the original default value. - if (! $oldColumn instanceof Column) { - return false; - } - // We only need to drop an existing default constraint if we know the // column was defined with a default value before. - if ($oldColumn->getDefault() === null) { + if ($columnDiff->getOldColumn()->getDefault() === null) { return false; } @@ -797,32 +568,27 @@ private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff * as column comments are stored in the same property there when * specifying a column's "Description" attribute. * - * @param string $tableName The quoted table name to which the column belongs. - * @param string $columnName The quoted column name to alter the comment for. - * @param string|null $comment The column's comment. - * - * @return string + * @param string $tableName The quoted table name to which the column belongs. + * @param string $columnName The quoted column name to alter the comment for. + * @param string $comment The column's comment. */ - protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) + protected function getAlterColumnCommentSQL(string $tableName, string $columnName, string $comment): string { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); + if (str_contains($tableName, '.')) { + [$schemaName, $tableName] = explode('.', $tableName); } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); + $schemaName = 'dbo'; } return $this->getUpdateExtendedPropertySQL( 'MS_Description', $comment, 'SCHEMA', - $schemaSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)), 'TABLE', - $tableSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)), 'COLUMN', - $columnName, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)), ); } @@ -839,35 +605,30 @@ protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) * * @param string $tableName The quoted table name to which the column belongs. * @param string $columnName The quoted column name to drop the comment for. - * - * @return string */ - protected function getDropColumnCommentSQL($tableName, $columnName) + protected function getDropColumnCommentSQL(string $tableName, string $columnName): string { - if (strpos($tableName, '.') !== false) { - [$schemaSQL, $tableSQL] = explode('.', $tableName); - $schemaSQL = $this->quoteStringLiteral($schemaSQL); - $tableSQL = $this->quoteStringLiteral($tableSQL); + if (str_contains($tableName, '.')) { + [$schemaName, $tableName] = explode('.', $tableName); } else { - $schemaSQL = "'dbo'"; - $tableSQL = $this->quoteStringLiteral($tableName); + $schemaName = 'dbo'; } return $this->getDropExtendedPropertySQL( 'MS_Description', 'SCHEMA', - $schemaSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($schemaName)), 'TABLE', - $tableSQL, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)), 'COLUMN', - $columnName, + $this->quoteStringLiteral($this->unquoteSingleIdentifier($columnName)), ); } /** * {@inheritDoc} */ - protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) + protected function getRenameIndexSQL(string $oldIndexName, Index $index, string $tableName): array { return [sprintf( "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", @@ -881,8 +642,6 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) /** * Returns the SQL statement for adding an extended property to a database object. * - * @internal The method should be only used from within the SQLServerPlatform class hierarchy. - * * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx * * @param string $name The name of the property to add. @@ -893,31 +652,30 @@ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string */ - public function getAddExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { + protected function getAddExtendedPropertySQL( + string $name, + ?string $value = null, + ?string $level0Type = null, + ?string $level0Name = null, + ?string $level1Type = null, + ?string $level1Name = null, + ?string $level2Type = null, + ?string $level2Name = null, + ): string { return 'EXEC sp_addextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral($value ?? '') . ', ' . + 'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name . + ($level2Type !== null || $level2Name !== null + ? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name + : '' + ); } /** * Returns the SQL statement for dropping an extended property from a database object. * - * @internal The method should be only used from within the SQLServerPlatform class hierarchy. - * * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx * * @param string $name The name of the property to drop. @@ -927,30 +685,29 @@ public function getAddExtendedPropertySQL( * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string */ - public function getDropExtendedPropertySQL( - $name, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { + protected function getDropExtendedPropertySQL( + string $name, + ?string $level0Type = null, + ?string $level0Name = null, + ?string $level1Type = null, + ?string $level1Name = null, + ?string $level2Type = null, + ?string $level2Name = null, + ): string { return 'EXEC sp_dropextendedproperty ' . 'N' . $this->quoteStringLiteral($name) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + 'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name . + ($level2Type !== null || $level2Name !== null + ? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name + : '' + ); } /** * Returns the SQL statement for updating an extended property of a database object. * - * @internal The method should be only used from within the SQLServerPlatform class hierarchy. - * * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx * * @param string $name The name of the property to update. @@ -961,208 +718,65 @@ public function getDropExtendedPropertySQL( * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. - * - * @return string */ - public function getUpdateExtendedPropertySQL( - $name, - $value = null, - $level0Type = null, - $level0Name = null, - $level1Type = null, - $level1Name = null, - $level2Type = null, - $level2Name = null - ) { + protected function getUpdateExtendedPropertySQL( + string $name, + ?string $value = null, + ?string $level0Type = null, + ?string $level0Name = null, + ?string $level1Type = null, + ?string $level1Name = null, + ?string $level2Type = null, + ?string $level2Name = null, + ): string { return 'EXEC sp_updateextendedproperty ' . - 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . - 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . - 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; + 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral($value ?? '') . ', ' . + 'N' . $this->quoteStringLiteral($level0Type ?? '') . ', ' . $level0Name . ', ' . + 'N' . $this->quoteStringLiteral($level1Type ?? '') . ', ' . $level1Name . + ($level2Type !== null || $level2Name !== null + ? ', N' . $this->quoteStringLiteral($level2Type ?? '') . ', ' . $level2Name + : '' + ); } - /** - * {@inheritDoc} - */ - public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) + public function getEmptyIdentityInsertSQL(string $quotedTableName, string $quotedIdentifierColumnName): string { return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; } - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - 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, SCHEMA_NAME (uid) AS schema_name FROM sysobjects' - . " WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - return "SELECT col.name, - type.name AS type, - col.max_length AS length, - ~col.is_nullable AS notnull, - def.definition AS [default], - col.scale, - col.precision, - col.is_identity AS autoincrement, - col.collation_name AS collation, - CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type - FROM sys.columns AS col - JOIN sys.types AS type - ON col.user_type_id = type.user_type_id - JOIN sys.objects AS obj - ON col.object_id = obj.object_id - JOIN sys.schemas AS scm - ON obj.schema_id = scm.schema_id - LEFT JOIN sys.default_constraints def - ON col.default_object_id = def.object_id - AND col.object_id = def.parent_object_id - LEFT JOIN sys.extended_properties AS prop - ON obj.object_id = prop.major_id - AND col.column_id = prop.minor_id - AND prop.name = 'MS_Description' - WHERE obj.type = 'U' - AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - return 'SELECT f.name AS ForeignKey, - SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, - OBJECT_NAME (f.parent_object_id) AS TableName, - COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, - SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, - OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, - COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, - f.delete_referential_action_desc, - f.update_referential_action_desc - FROM sys.foreign_keys AS f - INNER JOIN sys.foreign_key_columns AS fc - INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id - ON f.OBJECT_ID = fc.constraint_object_id - WHERE ' . - $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)') . - ' ORDER BY fc.constraint_column_id'; - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - return "SELECT idx.name AS key_name, - col.name AS column_name, - ~idx.is_unique AS non_unique, - idx.is_primary_key AS [primary], - CASE idx.type - WHEN '1' THEN 'clustered' - WHEN '2' THEN 'nonclustered' - ELSE NULL - END AS flags - FROM sys.tables AS tbl - JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id - JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id - JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id - JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id - WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' - ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string { return "SELECT name, definition FROM sysobjects INNER JOIN sys.sql_modules ON sysobjects.id = sys.sql_modules.object_id WHERE type = 'V' ORDER BY name"; } - /** - * Returns the where clause to filter schema and table name in a query. - * - * @param string $table The full qualified name of the table. - * @param string $schemaColumn The name of the column to compare the schema to in the where clause. - * @param string $tableColumn The name of the column to compare the table to in the where clause. - */ - private function getTableWhereClause($table, $schemaColumn, $tableColumn): string + public function getLocateExpression(string $string, string $substring, ?string $start = null): string { - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - $table = $this->quoteStringLiteral($table); - } else { - $schema = 'SCHEMA_NAME()'; - $table = $this->quoteStringLiteral($table); + if ($start === null) { + return sprintf('CHARINDEX(%s, %s)', $substring, $string); } - return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); + return sprintf('CHARINDEX(%s, %s, %s)', $substring, $string, $start); } - /** - * {@inheritDoc} - */ - public function getLocateExpression($str, $substr, $startPos = false) + public function getModExpression(string $dividend, string $divisor): string { - if ($startPos === false) { - return 'CHARINDEX(' . $substr . ', ' . $str . ')'; - } - - return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; + return $dividend . ' % ' . $divisor; } - /** - * {@inheritDoc} - */ - public function getModExpression($expression1, $expression2) - { - return $expression1 . ' % ' . $expression2; - } - - /** - * {@inheritDoc} - */ - public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) - { - if ($char === false) { - switch ($mode) { - case TrimMode::LEADING: - $trimFn = 'LTRIM'; - break; - - case TrimMode::TRAILING: - $trimFn = 'RTRIM'; - break; - - default: - return 'LTRIM(RTRIM(' . $str . '))'; - } - - return $trimFn . '(' . $str . ')'; + public function getTrimExpression( + string $str, + TrimMode $mode = TrimMode::UNSPECIFIED, + ?string $char = null, + ): string { + if ($char === null) { + return match ($mode) { + TrimMode::LEADING => 'LTRIM(' . $str . ')', + TrimMode::TRAILING => 'RTRIM(' . $str . ')', + default => 'LTRIM(RTRIM(' . $str . '))', + }; } $pattern = "'%[^' + " . $char . " + ']%'"; @@ -1181,59 +795,29 @@ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = f . ') - 1, null))) - 1, null))'; } - /** - * {@inheritDoc} - */ - public function getConcatExpression() + public function getConcatExpression(string ...$string): string { - return sprintf('CONCAT(%s)', implode(', ', func_get_args())); + return sprintf('CONCAT(%s)', implode(', ', $string)); } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListDatabasesSQL() + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListDatabasesSQL(): string { return 'SELECT * FROM sys.databases'; } - /** - * {@inheritDoc} - * - * @deprecated Use {@see SQLServerSchemaManager::listSchemaNames()} instead. - */ - public function getListNamespacesSQL() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'SQLServerPlatform::getListNamespacesSQL() is deprecated,' - . ' use SQLServerSchemaManager::listSchemaNames() instead.', - ); - - return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; - } - - /** - * {@inheritDoc} - */ - public function getSubstringExpression($string, $start, $length = null) + public function getSubstringExpression(string $string, string $start, ?string $length = null): string { - if ($length !== null) { - return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; + if ($length === null) { + return sprintf('SUBSTRING(%s, %s, LEN(%s) - %s + 1)', $string, $start, $string, $start); } - return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; + return sprintf('SUBSTRING(%s, %s, %s)', $string, $start, $length); } - /** - * {@inheritDoc} - */ - public function getLengthExpression($column) + public function getLengthExpression(string $string): string { - return 'LEN(' . $column . ')'; + return 'LEN(' . $string . ')'; } public function getCurrentDatabaseExpression(): string @@ -1241,10 +825,7 @@ public function getCurrentDatabaseExpression(): string return 'DB_NAME()'; } - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string { return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } @@ -1252,7 +833,7 @@ public function getSetTransactionIsolationSQL($level) /** * {@inheritDoc} */ - public function getIntegerTypeDeclarationSQL(array $column) + public function getIntegerTypeDeclarationSQL(array $column): string { return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1260,7 +841,7 @@ public function getIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBigIntTypeDeclarationSQL(array $column) + public function getBigIntTypeDeclarationSQL(array $column): string { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1268,7 +849,7 @@ public function getBigIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getSmallIntTypeDeclarationSQL(array $column) + public function getSmallIntTypeDeclarationSQL(array $column): string { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } @@ -1276,7 +857,7 @@ public function getSmallIntTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getGuidTypeDeclarationSQL(array $column) + public function getGuidTypeDeclarationSQL(array $column): string { return 'UNIQUEIDENTIFIER'; } @@ -1284,83 +865,49 @@ public function getGuidTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTzTypeDeclarationSQL(array $column) + public function getDateTimeTzTypeDeclarationSQL(array $column): string { return 'DATETIMEOFFSET(6)'; } - /** - * {@inheritDoc} - */ - public function getAsciiStringTypeDeclarationSQL(array $column): string + protected function getCharTypeDeclarationSQLSnippet(?int $length): string { - $length = $column['length'] ?? null; - - if (! isset($column['fixed'])) { - return sprintf('VARCHAR(%d)', $length ?? 255); - } - - return sprintf('CHAR(%d)', $length ?? 255); - } + $sql = 'NCHAR'; - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) - { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default string column length on SQL Server is deprecated' - . ', specify the length explicitly.', - ); + if ($length !== null) { + $sql .= sprintf('(%d)', $length); } - return $fixed - ? 'NCHAR(' . ($length > 0 ? $length : 255) . ')' - : 'NVARCHAR(' . ($length > 0 ? $length : 255) . ')'; + return $sql; } - /** - * {@inheritDoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) - { - if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length on SQL Server is deprecated' - . ', specify the length explicitly.', - ); + protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string + { + if ($length === null) { + throw ColumnLengthRequired::new($this, 'NVARCHAR'); } - return $fixed - ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' - : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; + return sprintf('NVARCHAR(%d)', $length); } /** * {@inheritDoc} - * - * @deprecated */ - public function getBinaryMaxLength() + public function getAsciiStringTypeDeclarationSQL(array $column): string { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'SQLServerPlatform::getBinaryMaxLength() is deprecated.', - ); + $length = $column['length'] ?? null; + + if (empty($column['fixed'])) { + return parent::getVarcharTypeDeclarationSQLSnippet($length); + } - return 8000; + return parent::getCharTypeDeclarationSQLSnippet($length); } /** * {@inheritDoc} */ - public function getClobTypeDeclarationSQL(array $column) + public function getClobTypeDeclarationSQL(array $column): string { return 'VARCHAR(MAX)'; } @@ -1368,7 +915,7 @@ public function getClobTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string { return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; } @@ -1376,7 +923,7 @@ protected function _getCommonIntegerTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTimeTypeDeclarationSQL(array $column) + public function getDateTimeTypeDeclarationSQL(array $column): string { // 3 - microseconds precision length // http://msdn.microsoft.com/en-us/library/ms187819.aspx @@ -1386,7 +933,7 @@ public function getDateTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getDateTypeDeclarationSQL(array $column) + public function getDateTypeDeclarationSQL(array $column): string { return 'DATE'; } @@ -1394,7 +941,7 @@ public function getDateTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getTimeTypeDeclarationSQL(array $column) + public function getTimeTypeDeclarationSQL(array $column): string { return 'TIME(0)'; } @@ -1402,15 +949,12 @@ public function getTimeTypeDeclarationSQL(array $column) /** * {@inheritDoc} */ - public function getBooleanTypeDeclarationSQL(array $column) + public function getBooleanTypeDeclarationSQL(array $column): string { return 'BIT'; } - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string { if ($limit === null && $offset <= 0) { return $query; @@ -1443,10 +987,7 @@ protected function doModifyLimitQuery($query, $limit, $offset) return $query; } - /** - * {@inheritDoc} - */ - public function convertBooleans($item) + public function convertBooleans(mixed $item): mixed { if (is_array($item)) { foreach ($item as $key => $value) { @@ -1463,66 +1004,37 @@ public function convertBooleans($item) return $item; } - /** - * {@inheritDoc} - */ - public function getCreateTemporaryTableSnippetSQL() + public function getCreateTemporaryTableSnippetSQL(): string { return 'CREATE TABLE'; } - /** - * {@inheritDoc} - */ - public function getTemporaryTableName($tableName) + public function getTemporaryTableName(string $tableName): string { return '#' . $tableName; } - /** - * {@inheritDoc} - */ - public function getDateTimeFormatString() + public function getDateTimeFormatString(): string { return 'Y-m-d H:i:s.u'; } - /** - * {@inheritDoc} - */ - public function getDateFormatString() + public function getDateFormatString(): string { return 'Y-m-d'; } - /** - * {@inheritDoc} - */ - public function getTimeFormatString() + public function getTimeFormatString(): string { return 'H:i:s'; } - /** - * {@inheritDoc} - */ - public function getDateTimeTzFormatString() + public function getDateTimeTzFormatString(): string { return 'Y-m-d H:i:s.u P'; } - /** - * {@inheritDoc} - */ - public function getName() - { - return 'mssql'; - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() + protected function initializeDoctrineTypeMappings(): void { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, @@ -1558,36 +1070,23 @@ protected function initializeDoctrineTypeMappings() ]; } - /** - * {@inheritDoc} - */ - public function createSavePoint($savepoint) + public function createSavePoint(string $savepoint): string { return 'SAVE TRANSACTION ' . $savepoint; } - /** - * {@inheritDoc} - */ - public function releaseSavePoint($savepoint) + public function releaseSavePoint(string $savepoint): string { return ''; } - /** - * {@inheritDoc} - */ - public function rollbackSavePoint($savepoint) + public function rollbackSavePoint(string $savepoint): string { return 'ROLLBACK TRANSACTION ' . $savepoint; } - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getForeignKeyReferentialActionSQL($action) + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getForeignKeyReferentialActionSQL(string $action): string { // RESTRICT is not supported, therefore falling back to NO ACTION. if (strtoupper($action) === 'RESTRICT') { @@ -1597,63 +1096,27 @@ public function getForeignKeyReferentialActionSQL($action) return parent::getForeignKeyReferentialActionSQL($action); } - public function appendLockHint(string $fromClause, int $lockMode): string - { - switch ($lockMode) { - case LockMode::NONE: - case LockMode::OPTIMISTIC: - return $fromClause; - - case LockMode::PESSIMISTIC_READ: - return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; - - case LockMode::PESSIMISTIC_WRITE: - return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; - - default: - throw InvalidLockMode::fromLockMode($lockMode); - } - } - - /** - * {@inheritDoc} - * - * @deprecated This API is not portable. - */ - public function getForUpdateSQL() + public function appendLockHint(string $fromClause, LockMode $lockMode): string { - return ' '; + return match ($lockMode) { + LockMode::NONE, + LockMode::OPTIMISTIC => $fromClause, + LockMode::PESSIMISTIC_READ => $fromClause . ' WITH (HOLDLOCK, ROWLOCK)', + LockMode::PESSIMISTIC_WRITE => $fromClause . ' WITH (UPDLOCK, ROWLOCK)', + }; } - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() + protected function createReservedKeywordsList(): KeywordList { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'SQLServerPlatform::getReservedKeywordsClass() is deprecated,' - . ' use SQLServerPlatform::createReservedKeywordsList() instead.', - ); - - return Keywords\SQLServer2012Keywords::class; + return new SQLServerKeywords(); } - /** - * {@inheritDoc} - */ - public function quoteSingleIdentifier($str) + public function quoteSingleIdentifier(string $str): string { return '[' . str_replace(']', ']]', $str) . ']'; } - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string { $tableIdentifier = new Identifier($tableName); @@ -1663,7 +1126,7 @@ public function getTruncateTableSQL($tableName, $cascade = false) /** * {@inheritDoc} */ - public function getBlobTypeDeclarationSQL(array $column) + public function getBlobTypeDeclarationSQL(array $column): string { return 'VARBINARY(MAX)'; } @@ -1673,53 +1136,27 @@ public function getBlobTypeDeclarationSQL(array $column) * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ - public function getColumnDeclarationSQL($name, array $column) + public function getColumnDeclarationSQL(string $name, array $column): string { if (isset($column['columnDefinition'])) { - $columnDef = $this->getCustomTypeDeclarationSQL($column); + $declaration = $column['columnDefinition']; } else { $collation = ! empty($column['collation']) ? ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; - if (! empty($column['unique'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', - ); - - $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); - } else { - $unique = ''; - } - - if (! empty($column['check'])) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5656', - 'The usage of the "check" column property is deprecated.', - ); - - $check = ' ' . $column['check']; - } else { - $check = ''; - } - - $typeDecl = $column['type']->getSQLDeclaration($column, $this); - $columnDef = $typeDecl . $collation . $notnull . $unique . $check; + $typeDecl = $column['type']->getSQLDeclaration($column, $this); + $declaration = $typeDecl . $collation . $notnull; } - return $name . ' ' . $columnDef; + return $name . ' ' . $declaration; } /** - * {@inheritDoc} - * * SQL Server does not support quoting collation identifiers. */ - public function getColumnCollationDeclarationSQL($collation) + public function getColumnCollationDeclarationSQL(string $collation): string { return 'COLLATE ' . $collation; } @@ -1739,64 +1176,19 @@ protected function getLikeWildcardCharacters(): string return parent::getLikeWildcardCharacters() . '[]^'; } - /** - * Returns a unique default constraint name for a table and column. - * - * @param string $table Name of the table to generate the unique default constraint name for. - * @param string $column Name of the column in the table to generate the unique default constraint name for. - */ - private function generateDefaultConstraintName($table, $column): string - { - return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); - } - - /** - * Returns a hash value for a given identifier. - * - * @param string $identifier Identifier to generate a hash value for. - */ - private function generateIdentifierName($identifier): string - { - // Always generate name for unquoted identifiers to ensure consistency. - $identifier = new Identifier($identifier); - - return strtoupper(dechex(crc32($identifier->getName()))); - } - - protected function getCommentOnTableSQL(string $tableName, ?string $comment): string - { - return sprintf( - <<<'SQL' - EXEC sys.sp_addextendedproperty @name=N'MS_Description', - @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', - @level1type=N'TABLE', @level1name=N%s - SQL - , - $this->quoteStringLiteral((string) $comment), - $this->quoteStringLiteral($tableName), - ); - } - - /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ - public function getListTableMetadataSQL(string $table): string + protected function getCommentOnTableSQL(string $tableName, string $comment): string { - return sprintf( - <<<'SQL' - SELECT - p.value AS [table_comment] - FROM - sys.tables AS tbl - INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 - WHERE - (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') - SQL - , - $this->quoteStringLiteral($table), + return $this->getAddExtendedPropertySQL( + 'MS_Description', + $comment, + 'SCHEMA', + $this->quoteStringLiteral('dbo'), + 'TABLE', + $this->quoteStringLiteral($this->unquoteSingleIdentifier($tableName)), ); } - /** @param string $query */ - private function shouldAddOrderBy($query): bool + private function shouldAddOrderBy(string $query): bool { // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement // but can be in a newline diff --git a/doctrine/dbal/src/Platforms/SQLite/Comparator.php b/doctrine/dbal/src/Platforms/SQLite/Comparator.php index 5218871c6..f27e1b457 100644 --- a/doctrine/dbal/src/Platforms/SQLite/Comparator.php +++ b/doctrine/dbal/src/Platforms/SQLite/Comparator.php @@ -1,8 +1,10 @@ normalizeColumns($fromTable), - $this->normalizeColumns($toTable), - ); - } - - /** - * {@inheritDoc} - */ - public function diffTable(Table $fromTable, Table $toTable) - { - return parent::diffTable( - $this->normalizeColumns($fromTable), - $this->normalizeColumns($toTable), + $this->normalizeColumns($oldTable), + $this->normalizeColumns($newTable), ); } diff --git a/doctrine/dbal/src/Platforms/SQLitePlatform.php b/doctrine/dbal/src/Platforms/SQLitePlatform.php new file mode 100644 index 000000000..acec16317 --- /dev/null +++ b/doctrine/dbal/src/Platforms/SQLitePlatform.php @@ -0,0 +1,994 @@ + 'TRIM', + TrimMode::LEADING => 'LTRIM', + TrimMode::TRAILING => 'RTRIM', + }; + + $arguments = [$str]; + + if ($char !== null) { + $arguments[] = $char; + } + + return sprintf('%s(%s)', $trimFn, implode(', ', $arguments)); + } + + public function getSubstringExpression(string $string, string $start, ?string $length = null): string + { + if ($length === null) { + return sprintf('SUBSTR(%s, %s)', $string, $start); + } + + return sprintf('SUBSTR(%s, %s, %s)', $string, $start, $length); + } + + public function getLocateExpression(string $string, string $substring, ?string $start = null): string + { + if ($start === null || $start === '1') { + return sprintf('INSTR(%s, %s)', $string, $substring); + } + + return sprintf( + 'CASE WHEN INSTR(SUBSTR(%1$s, %3$s), %2$s) > 0 THEN INSTR(SUBSTR(%1$s, %3$s), %2$s) + %3$s - 1 ELSE 0 END', + $string, + $substring, + $start, + ); + } + + protected function getDateArithmeticIntervalExpression( + string $date, + string $operator, + string $interval, + DateIntervalUnit $unit, + ): string { + switch ($unit) { + case DateIntervalUnit::WEEK: + $interval = $this->multiplyInterval($interval, 7); + $unit = DateIntervalUnit::DAY; + break; + + case DateIntervalUnit::QUARTER: + $interval = $this->multiplyInterval($interval, 3); + $unit = DateIntervalUnit::MONTH; + break; + } + + return 'DATETIME(' . $date . ',' . $this->getConcatExpression( + $this->quoteStringLiteral($operator), + $interval, + $this->quoteStringLiteral(' ' . $unit->value), + ) . ')'; + } + + public function getDateDiffExpression(string $date1, string $date2): string + { + return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2); + } + + /** + * {@inheritDoc} + * + * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string + * as an indicator of an implicitly selected database. + * + * @link https://www.sqlite.org/lang_select.html + * @see Connection::getDatabase() + */ + public function getCurrentDatabaseExpression(): string + { + return "'main'"; + } + + /** @link https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql */ + public function createSelectSQLBuilder(): SelectSQLBuilder + { + return new DefaultSelectSQLBuilder($this, null, null); + } + + protected function _getTransactionIsolationLevelSQL(TransactionIsolationLevel $level): string + { + return match ($level) { + TransactionIsolationLevel::READ_UNCOMMITTED => '0', + TransactionIsolationLevel::READ_COMMITTED, + TransactionIsolationLevel::REPEATABLE_READ, + TransactionIsolationLevel::SERIALIZABLE => '1', + }; + } + + public function getSetTransactionIsolationSQL(TransactionIsolationLevel $level): string + { + return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); + } + + /** + * {@inheritDoc} + */ + public function getBooleanTypeDeclarationSQL(array $column): string + { + return 'BOOLEAN'; + } + + /** + * {@inheritDoc} + */ + public function getIntegerTypeDeclarationSQL(array $column): string + { + return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getBigIntTypeDeclarationSQL(array $column): string + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT fields. + if (! empty($column['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($column); + } + + return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getSmallIntTypeDeclarationSQL(array $column): string + { + // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT fields. + if (! empty($column['autoincrement'])) { + return $this->getIntegerTypeDeclarationSQL($column); + } + + return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); + } + + /** + * {@inheritDoc} + */ + public function getDateTimeTypeDeclarationSQL(array $column): string + { + return 'DATETIME'; + } + + /** + * {@inheritDoc} + */ + public function getDateTypeDeclarationSQL(array $column): string + { + return 'DATE'; + } + + /** + * {@inheritDoc} + */ + public function getTimeTypeDeclarationSQL(array $column): string + { + return 'TIME'; + } + + /** + * {@inheritDoc} + */ + protected function _getCommonIntegerTypeDeclarationSQL(array $column): string + { + // sqlite autoincrement is only possible for the primary key + if (! empty($column['autoincrement'])) { + return ' PRIMARY KEY AUTOINCREMENT'; + } + + return ! empty($column['unsigned']) ? ' UNSIGNED' : ''; + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey): string + { + return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( + $foreignKey->getQuotedLocalColumns($this), + $foreignKey->getQuotedForeignTableName($this), + $foreignKey->getQuotedForeignColumns($this), + $foreignKey->getName(), + $foreignKey->getOptions(), + )); + } + + /** + * {@inheritDoc} + */ + protected function _getCreateTableSQL(string $name, array $columns, array $options = []): array + { + $queryFields = $this->getColumnDeclarationListSQL($columns); + + if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { + foreach ($options['uniqueConstraints'] as $definition) { + $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($definition); + } + } + + $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options); + + if (isset($options['foreignKeys'])) { + foreach ($options['foreignKeys'] as $foreignKey) { + $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey); + } + } + + $tableComment = ''; + if (isset($options['comment'])) { + $comment = trim($options['comment'], " '"); + + $tableComment = $this->getInlineTableCommentSQL($comment); + } + + $query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')']; + + if (isset($options['alter']) && $options['alter'] === true) { + return $query; + } + + if (isset($options['indexes']) && ! empty($options['indexes'])) { + foreach ($options['indexes'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + if (isset($options['unique']) && ! empty($options['unique'])) { + foreach ($options['unique'] as $indexDef) { + $query[] = $this->getCreateIndexSQL($indexDef, $name); + } + } + + return $query; + } + + /** + * Generate a PRIMARY KEY definition if no autoincrement value is used + * + * @param mixed[][] $columns + * @param mixed[] $options + */ + private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string + { + if (empty($options['primary'])) { + return ''; + } + + $keyColumns = array_unique(array_values($options['primary'])); + + foreach ($keyColumns as $keyColumn) { + foreach ($columns as $column) { + if ($column['name'] === $keyColumn && ! empty($column['autoincrement'])) { + return ''; + } + } + } + + return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; + } + + protected function getBinaryTypeDeclarationSQLSnippet(?int $length): string + { + return 'BLOB'; + } + + protected function getVarcharTypeDeclarationSQLSnippet(?int $length): string + { + $sql = 'VARCHAR'; + + if ($length !== null) { + $sql .= sprintf('(%d)', $length); + } + + return $sql; + } + + protected function getVarbinaryTypeDeclarationSQLSnippet(?int $length): string + { + return 'BLOB'; + } + + /** + * {@inheritDoc} + */ + public function getClobTypeDeclarationSQL(array $column): string + { + return 'CLOB'; + } + + /** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ + public function getListViewsSQL(string $database): string + { + return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey): string + { + $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); + + if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) { + $query .= ' NOT'; + } + + $query .= ' DEFERRABLE'; + $query .= ' INITIALLY'; + + if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) { + $query .= ' DEFERRED'; + } else { + $query .= ' IMMEDIATE'; + } + + return $query; + } + + public function supportsIdentityColumns(): bool + { + return true; + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsColumnCollation(): bool + { + return true; + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function supportsInlineColumnComments(): bool + { + return true; + } + + public function getTruncateTableSQL(string $tableName, bool $cascade = false): string + { + $tableIdentifier = new Identifier($tableName); + + return 'DELETE FROM ' . $tableIdentifier->getQuotedName($this); + } + + /** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ + public function getInlineColumnCommentSQL(string $comment): string + { + if ($comment === '') { + return ''; + } + + return '--' . str_replace("\n", "\n--", $comment) . "\n"; + } + + private function getInlineTableCommentSQL(string $comment): string + { + return $this->getInlineColumnCommentSQL($comment); + } + + protected function initializeDoctrineTypeMappings(): void + { + $this->doctrineTypeMapping = [ + 'bigint' => 'bigint', + 'bigserial' => 'bigint', + 'blob' => 'blob', + 'boolean' => 'boolean', + 'char' => 'string', + 'clob' => 'text', + 'date' => 'date', + 'datetime' => 'datetime', + 'decimal' => 'decimal', + 'double' => 'float', + 'double precision' => 'float', + 'float' => 'float', + 'image' => 'string', + 'int' => 'integer', + 'integer' => 'integer', + 'longtext' => 'text', + 'longvarchar' => 'string', + 'mediumint' => 'integer', + 'mediumtext' => 'text', + 'ntext' => 'string', + 'numeric' => 'decimal', + 'nvarchar' => 'string', + 'real' => 'float', + 'serial' => 'integer', + 'smallint' => 'smallint', + 'string' => 'string', + 'text' => 'text', + 'time' => 'time', + 'timestamp' => 'datetime', + 'tinyint' => 'boolean', + 'tinytext' => 'text', + 'varchar' => 'string', + 'varchar2' => 'string', + ]; + } + + protected function createReservedKeywordsList(): KeywordList + { + return new SQLiteKeywords(); + } + + /** + * {@inheritDoc} + */ + protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff): array + { + return []; + } + + /** + * {@inheritDoc} + */ + protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff): array + { + $table = $diff->getOldTable(); + + $sql = []; + + foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) { + if ($index->isPrimary()) { + continue; + } + + $sql[] = $this->getCreateIndexSQL($index, $table->getQuotedName($this)); + } + + return $sql; + } + + protected function doModifyLimitQuery(string $query, ?int $limit, int $offset): string + { + if ($limit === null && $offset > 0) { + $limit = -1; + } + + return parent::doModifyLimitQuery($query, $limit, $offset); + } + + /** + * {@inheritDoc} + */ + public function getBlobTypeDeclarationSQL(array $column): string + { + return 'BLOB'; + } + + public function getTemporaryTableName(string $tableName): string + { + return $tableName; + } + + /** + * {@inheritDoc} + */ + public function getCreateTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + $sql = array_merge($sql, $this->getCreateTableSQL($table)); + } + + return $sql; + } + + /** {@inheritDoc} */ + public function getCreateIndexSQL(Index $index, string $table): string + { + $name = $index->getQuotedName($this); + $columns = $index->getColumns(); + + if (count($columns) === 0) { + throw new InvalidArgumentException(sprintf( + 'Incomplete or invalid index definition %s on table %s', + $name, + $table, + )); + } + + if ($index->isPrimary()) { + return $this->getCreatePrimaryKeySQL($index, $table); + } + + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $name = $schema . '.' . $name; + } + + $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; + $query .= ' (' . implode(', ', $index->getQuotedColumns($this)) . ')' . $this->getPartialIndexSQL($index); + + return $query; + } + + /** + * {@inheritDoc} + */ + public function getDropTablesSQL(array $tables): array + { + $sql = []; + + foreach ($tables as $table) { + $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); + } + + return $sql; + } + + public function getCreatePrimaryKeySQL(Index $index, string $table): string + { + throw NotSupported::new(__METHOD__); + } + + public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, string $table): string + { + throw NotSupported::new(__METHOD__); + } + + public function getDropForeignKeySQL(string $foreignKey, string $table): string + { + throw NotSupported::new(__METHOD__); + } + + /** + * {@inheritDoc} + */ + public function getAlterTableSQL(TableDiff $diff): array + { + $sql = $this->getSimpleAlterTableSQL($diff); + if ($sql !== false) { + return $sql; + } + + $table = $diff->getOldTable(); + + $columns = []; + $oldColumnNames = []; + $newColumnNames = []; + $columnSql = []; + + foreach ($table->getColumns() as $column) { + $columnName = strtolower($column->getName()); + $columns[$columnName] = $column; + $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); + } + + foreach ($diff->getDroppedColumns() as $column) { + $columnName = strtolower($column->getName()); + if (! isset($columns[$columnName])) { + continue; + } + + unset( + $columns[$columnName], + $oldColumnNames[$columnName], + $newColumnNames[$columnName], + ); + } + + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { + $oldColumnName = strtolower($oldColumnName); + + $columns = $this->replaceColumn( + $table->getName(), + $columns, + $oldColumnName, + $column, + ); + + if (! isset($newColumnNames[$oldColumnName])) { + continue; + } + + $newColumnNames[$oldColumnName] = $column->getQuotedName($this); + } + + foreach ($diff->getModifiedColumns() as $columnDiff) { + $oldColumnName = strtolower($columnDiff->getOldColumn()->getName()); + $newColumn = $columnDiff->getNewColumn(); + + $columns = $this->replaceColumn( + $table->getName(), + $columns, + $oldColumnName, + $newColumn, + ); + + if (! isset($newColumnNames[$oldColumnName])) { + continue; + } + + $newColumnNames[$oldColumnName] = $newColumn->getQuotedName($this); + } + + foreach ($diff->getAddedColumns() as $column) { + $columns[strtolower($column->getName())] = $column; + } + + $tableName = $table->getName(); + $pos = strpos($tableName, '.'); + if ($pos !== false) { + $tableName = substr($tableName, $pos + 1); + } + + $dataTable = new Table('__temp__' . $tableName); + + $newTable = new Table( + $table->getQuotedName($this), + $columns, + $this->getPrimaryIndexInAlteredTable($diff, $table), + [], + $this->getForeignKeysInAlteredTable($diff, $table), + $table->getOptions(), + ); + + $newTable->addOption('alter', true); + + $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); + + $sql[] = sprintf( + 'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', + $dataTable->getQuotedName($this), + implode(', ', $oldColumnNames), + $table->getQuotedName($this), + ); + $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); + + $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); + $sql[] = sprintf( + 'INSERT INTO %s (%s) SELECT %s FROM %s', + $newTable->getQuotedName($this), + implode(', ', $newColumnNames), + implode(', ', $oldColumnNames), + $dataTable->getQuotedName($this), + ); + $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this)); + + return array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff), $columnSql); + } + + /** + * Replace the column with the given name with the new column. + * + * @param array $columns + * + * @return array + * + * @throws Exception + */ + private function replaceColumn(string $tableName, array $columns, string $columnName, Column $column): array + { + $keys = array_keys($columns); + $index = array_search($columnName, $keys, true); + + if ($index === false) { + throw ColumnDoesNotExist::new($columnName, $tableName); + } + + $values = array_values($columns); + + $keys[$index] = strtolower($column->getName()); + $values[$index] = $column; + + return array_combine($keys, $values); + } + + /** + * @return list|false + * + * @throws Exception + */ + private function getSimpleAlterTableSQL(TableDiff $diff): array|false + { + if ( + count($diff->getModifiedColumns()) > 0 + || count($diff->getDroppedColumns()) > 0 + || count($diff->getRenamedColumns()) > 0 + || count($diff->getAddedIndexes()) > 0 + || count($diff->getModifiedIndexes()) > 0 + || count($diff->getDroppedIndexes()) > 0 + || count($diff->getRenamedIndexes()) > 0 + || count($diff->getAddedForeignKeys()) > 0 + || count($diff->getModifiedForeignKeys()) > 0 + || count($diff->getDroppedForeignKeys()) > 0 + ) { + return false; + } + + $table = $diff->getOldTable(); + + $sql = []; + $columnSql = []; + + foreach ($diff->getAddedColumns() as $column) { + $definition = array_merge([ + 'unique' => null, + 'autoincrement' => null, + 'default' => null, + ], $column->toArray()); + + $type = $definition['type']; + + /** @psalm-suppress RiskyTruthyFalsyComparison */ + switch (true) { + case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']: + case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL(): + case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL(): + case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL(): + return false; + } + + $definition['name'] = $column->getQuotedName($this); + + $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' + . $this->getColumnDeclarationSQL($definition['name'], $definition); + } + + return array_merge($sql, $columnSql); + } + + /** @return string[] */ + private function getColumnNamesInAlteredTable(TableDiff $diff, Table $oldTable): array + { + $columns = []; + + foreach ($oldTable->getColumns() as $column) { + $columnName = $column->getName(); + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->getDroppedColumns() as $column) { + $columnName = strtolower($column->getName()); + if (! isset($columns[$columnName])) { + continue; + } + + unset($columns[$columnName]); + } + + foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { + $columnName = $column->getName(); + $columns[strtolower($oldColumnName)] = $columnName; + $columns[strtolower($columnName)] = $columnName; + } + + foreach ($diff->getModifiedColumns() as $columnDiff) { + $oldColumnName = $columnDiff->getOldColumn()->getName(); + $newColumnName = $columnDiff->getNewColumn()->getName(); + $columns[strtolower($oldColumnName)] = $newColumnName; + $columns[strtolower($newColumnName)] = $newColumnName; + } + + foreach ($diff->getAddedColumns() as $column) { + $columnName = $column->getName(); + $columns[strtolower($columnName)] = $columnName; + } + + return $columns; + } + + /** @return Index[] */ + private function getIndexesInAlteredTable(TableDiff $diff, Table $oldTable): array + { + $indexes = $oldTable->getIndexes(); + $columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable); + + foreach ($indexes as $key => $index) { + foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) { + if (strtolower($key) !== strtolower($oldIndexName)) { + continue; + } + + unset($indexes[$key]); + } + + $changed = false; + $indexColumns = []; + foreach ($index->getColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if (! isset($columnNames[$normalizedColumnName])) { + unset($indexes[$key]); + continue 2; + } + + $indexColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName === $columnNames[$normalizedColumnName]) { + continue; + } + + $changed = true; + } + + if (! $changed) { + continue; + } + + $indexes[$key] = new Index( + $index->getName(), + $indexColumns, + $index->isUnique(), + $index->isPrimary(), + $index->getFlags(), + ); + } + + foreach ($diff->getDroppedIndexes() as $index) { + $indexName = $index->getName(); + + if ($indexName === '') { + continue; + } + + unset($indexes[strtolower($indexName)]); + } + + foreach ( + array_merge( + $diff->getModifiedIndexes(), + $diff->getAddedIndexes(), + $diff->getRenamedIndexes(), + ) as $index + ) { + $indexName = $index->getName(); + + if ($indexName !== '') { + $indexes[strtolower($indexName)] = $index; + } else { + $indexes[] = $index; + } + } + + return $indexes; + } + + /** @return ForeignKeyConstraint[] */ + private function getForeignKeysInAlteredTable(TableDiff $diff, Table $oldTable): array + { + $foreignKeys = $oldTable->getForeignKeys(); + $columnNames = $this->getColumnNamesInAlteredTable($diff, $oldTable); + + foreach ($foreignKeys as $key => $constraint) { + $changed = false; + $localColumns = []; + foreach ($constraint->getLocalColumns() as $columnName) { + $normalizedColumnName = strtolower($columnName); + if (! isset($columnNames[$normalizedColumnName])) { + unset($foreignKeys[$key]); + continue 2; + } + + $localColumns[] = $columnNames[$normalizedColumnName]; + if ($columnName === $columnNames[$normalizedColumnName]) { + continue; + } + + $changed = true; + } + + if (! $changed) { + continue; + } + + $foreignKeys[$key] = new ForeignKeyConstraint( + $localColumns, + $constraint->getForeignTableName(), + $constraint->getForeignColumns(), + $constraint->getName(), + $constraint->getOptions(), + ); + } + + foreach ($diff->getDroppedForeignKeys() as $constraint) { + $constraintName = $constraint->getName(); + + if ($constraintName === '') { + continue; + } + + unset($foreignKeys[strtolower($constraintName)]); + } + + foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) { + $constraintName = $constraint->getName(); + + if ($constraintName !== '') { + $foreignKeys[strtolower($constraintName)] = $constraint; + } else { + $foreignKeys[] = $constraint; + } + } + + return $foreignKeys; + } + + /** @return Index[] */ + private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $oldTable): array + { + $primaryIndex = []; + + foreach ($this->getIndexesInAlteredTable($diff, $oldTable) as $index) { + if (! $index->isPrimary()) { + continue; + } + + $primaryIndex = [$index->getName() => $index]; + } + + return $primaryIndex; + } + + public function createSchemaManager(Connection $connection): SQLiteSchemaManager + { + return new SQLiteSchemaManager($connection, $this); + } +} diff --git a/doctrine/dbal/src/Platforms/SqlitePlatform.php b/doctrine/dbal/src/Platforms/SqlitePlatform.php deleted file mode 100644 index ea9a44413..000000000 --- a/doctrine/dbal/src/Platforms/SqlitePlatform.php +++ /dev/null @@ -1,1540 +0,0 @@ - 0 THEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') + ' . $startPos - . ' - 1 ELSE 0 END'; - } - - /** - * {@inheritDoc} - */ - protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) - { - switch ($unit) { - case DateIntervalUnit::SECOND: - case DateIntervalUnit::MINUTE: - case DateIntervalUnit::HOUR: - return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; - } - - switch ($unit) { - case DateIntervalUnit::WEEK: - $interval = $this->multiplyInterval((string) $interval, 7); - $unit = DateIntervalUnit::DAY; - break; - - case DateIntervalUnit::QUARTER: - $interval = $this->multiplyInterval((string) $interval, 3); - $unit = DateIntervalUnit::MONTH; - break; - } - - if (! is_numeric($interval)) { - $interval = "' || " . $interval . " || '"; - } - - return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; - } - - /** - * {@inheritDoc} - */ - public function getDateDiffExpression($date1, $date2) - { - return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2); - } - - /** - * {@inheritDoc} - * - * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string - * as an indicator of an implicitly selected database. - * - * @link https://www.sqlite.org/lang_select.html - * @see Connection::getDatabase() - */ - public function getCurrentDatabaseExpression(): string - { - return "'main'"; - } - - /** @link https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql */ - public function createSelectSQLBuilder(): SelectSQLBuilder - { - return new DefaultSelectSQLBuilder($this, null, null); - } - - /** - * {@inheritDoc} - */ - protected function _getTransactionIsolationLevelSQL($level) - { - switch ($level) { - case TransactionIsolationLevel::READ_UNCOMMITTED: - return '0'; - - case TransactionIsolationLevel::READ_COMMITTED: - case TransactionIsolationLevel::REPEATABLE_READ: - case TransactionIsolationLevel::SERIALIZABLE: - return '1'; - - default: - return parent::_getTransactionIsolationLevelSQL($level); - } - } - - /** - * {@inheritDoc} - */ - public function getSetTransactionIsolationSQL($level) - { - return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function prefersIdentityColumns() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/1519', - 'SqlitePlatform::prefersIdentityColumns() is deprecated.', - ); - - return true; - } - - /** - * {@inheritDoc} - */ - public function getBooleanTypeDeclarationSQL(array $column) - { - return 'BOOLEAN'; - } - - /** - * {@inheritDoc} - */ - public function getIntegerTypeDeclarationSQL(array $column) - { - return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getBigIntTypeDeclarationSQL(array $column) - { - // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT columns - if (! empty($column['autoincrement'])) { - return $this->getIntegerTypeDeclarationSQL($column); - } - - return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * @deprecated Use {@see getSmallIntTypeDeclarationSQL()} instead. - * - * @param array $column - * - * @return string - */ - public function getTinyIntTypeDeclarationSQL(array $column) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5511', - '%s is deprecated. Use getSmallIntTypeDeclarationSQL() instead.', - __METHOD__, - ); - - // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns - if (! empty($column['autoincrement'])) { - return $this->getIntegerTypeDeclarationSQL($column); - } - - return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getSmallIntTypeDeclarationSQL(array $column) - { - // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT columns - if (! empty($column['autoincrement'])) { - return $this->getIntegerTypeDeclarationSQL($column); - } - - return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * @deprecated Use {@see getIntegerTypeDeclarationSQL()} instead. - * - * @param array $column - * - * @return string - */ - public function getMediumIntTypeDeclarationSQL(array $column) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5511', - '%s is deprecated. Use getIntegerTypeDeclarationSQL() instead.', - __METHOD__, - ); - - // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns - if (! empty($column['autoincrement'])) { - return $this->getIntegerTypeDeclarationSQL($column); - } - - return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function getDateTimeTypeDeclarationSQL(array $column) - { - return 'DATETIME'; - } - - /** - * {@inheritDoc} - */ - public function getDateTypeDeclarationSQL(array $column) - { - return 'DATE'; - } - - /** - * {@inheritDoc} - */ - public function getTimeTypeDeclarationSQL(array $column) - { - return 'TIME'; - } - - /** - * {@inheritDoc} - */ - protected function _getCommonIntegerTypeDeclarationSQL(array $column) - { - // sqlite autoincrement is only possible for the primary key - if (! empty($column['autoincrement'])) { - return ' PRIMARY KEY AUTOINCREMENT'; - } - - return ! empty($column['unsigned']) ? ' UNSIGNED' : ''; - } - - /** - * Disables schema emulation. - * - * Schema emulation is enabled by default to maintain backwards compatibility. - * Disable it to opt-in to the behavior of DBAL 4. - * - * @deprecated Will be removed in DBAL 4.0. - */ - public function disableSchemaEmulation(): void - { - $this->schemaEmulationEnabled = false; - } - - private function emulateSchemaNamespacing(string $tableName): string - { - return $this->schemaEmulationEnabled - ? str_replace('.', '__', $tableName) - : $tableName; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) - { - return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( - $foreignKey->getQuotedLocalColumns($this), - $this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)), - $foreignKey->getQuotedForeignColumns($this), - $foreignKey->getName(), - $foreignKey->getOptions(), - )); - } - - /** - * {@inheritDoc} - */ - protected function _getCreateTableSQL($name, array $columns, array $options = []) - { - $name = $this->emulateSchemaNamespacing($name); - $queryFields = $this->getColumnDeclarationListSQL($columns); - - if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { - foreach ($options['uniqueConstraints'] as $constraintName => $definition) { - $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); - } - } - - $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options); - - if (isset($options['foreignKeys'])) { - foreach ($options['foreignKeys'] as $foreignKey) { - $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey); - } - } - - $tableComment = ''; - if (isset($options['comment'])) { - $comment = trim($options['comment'], " '"); - - $tableComment = $this->getInlineTableCommentSQL($comment); - } - - $query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')']; - - if (isset($options['alter']) && $options['alter'] === true) { - return $query; - } - - if (isset($options['indexes']) && ! empty($options['indexes'])) { - foreach ($options['indexes'] as $indexDef) { - $query[] = $this->getCreateIndexSQL($indexDef, $name); - } - } - - if (isset($options['unique']) && ! empty($options['unique'])) { - foreach ($options['unique'] as $indexDef) { - $query[] = $this->getCreateIndexSQL($indexDef, $name); - } - } - - return $query; - } - - /** - * Generate a PRIMARY KEY definition if no autoincrement value is used - * - * @param mixed[][] $columns - * @param mixed[] $options - */ - private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string - { - if (empty($options['primary'])) { - return ''; - } - - $keyColumns = array_unique(array_values($options['primary'])); - - foreach ($keyColumns as $keyColumn) { - if (! empty($columns[$keyColumn]['autoincrement'])) { - return ''; - } - } - - return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; - } - - /** - * {@inheritDoc} - */ - protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) - { - return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') - : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'TEXT'); - } - - /** - * {@inheritDoc} - */ - protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) - { - return 'BLOB'; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getBinaryMaxLength() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'SqlitePlatform::getBinaryMaxLength() is deprecated.', - ); - - return 0; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getBinaryDefaultLength() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3263', - 'Relying on the default binary column length is deprecated, specify the length explicitly.', - ); - - return 0; - } - - /** - * {@inheritDoc} - */ - public function getClobTypeDeclarationSQL(array $column) - { - return 'CLOB'; - } - - /** - * @deprecated - * - * {@inheritDoc} - */ - public function getListTableConstraintsSQL($table) - { - $table = $this->emulateSchemaNamespacing($table); - - return sprintf( - "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name", - $this->quoteStringLiteral($table), - ); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableColumnsSQL($table, $database = null) - { - $table = $this->emulateSchemaNamespacing($table); - - return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table)); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTableIndexesSQL($table, $database = null) - { - $table = $this->emulateSchemaNamespacing($table); - - return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table)); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * {@inheritDoc} - */ - public function getListTablesSQL() - { - return 'SELECT name FROM sqlite_master' - . " WHERE type = 'table'" - . " AND name != 'sqlite_sequence'" - . " AND name != 'geometry_columns'" - . " AND name != 'spatial_ref_sys'" - . ' UNION ALL SELECT name FROM sqlite_temp_master' - . " WHERE type = 'table' ORDER BY name"; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. - */ - public function getListViewsSQL($database) - { - return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) - { - $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); - - if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) { - $query .= ' NOT'; - } - - $query .= ' DEFERRABLE'; - $query .= ' INITIALLY'; - - if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) { - $query .= ' DEFERRED'; - } else { - $query .= ' IMMEDIATE'; - } - - return $query; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function supportsCreateDropDatabase() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5513', - '%s is deprecated.', - __METHOD__, - ); - - return false; - } - - /** - * {@inheritDoc} - */ - public function supportsIdentityColumns() - { - return true; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsColumnCollation() - { - return true; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function supportsInlineColumnComments() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4749', - 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.', - ); - - return 'sqlite'; - } - - /** - * {@inheritDoc} - */ - public function getTruncateTableSQL($tableName, $cascade = false) - { - $tableIdentifier = new Identifier($tableName); - $tableName = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this)); - - return 'DELETE FROM ' . $tableName; - } - - /** - * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). - * - * @deprecated The driver will use {@see sqrt()} in the next major release. - * - * @param int|float $value - * - * @return float - */ - public static function udfSqrt($value) - { - return sqrt($value); - } - - /** - * User-defined function for Sqlite that implements MOD(a, b). - * - * @deprecated The driver will use {@see UserDefinedFunctions::mod()} in the next major release. - * - * @param int $a - * @param int $b - * - * @return int - */ - public static function udfMod($a, $b) - { - return UserDefinedFunctions::mod($a, $b); - } - - /** - * @deprecated The driver will use {@see UserDefinedFunctions::locate()} in the next major release. - * - * @param string $str - * @param string $substr - * @param int $offset - * - * @return int - */ - public static function udfLocate($str, $substr, $offset = 0) - { - return UserDefinedFunctions::locate($str, $substr, $offset); - } - - /** - * {@inheritDoc} - * - * @deprecated This API is not portable. - */ - public function getForUpdateSQL() - { - return ''; - } - - /** - * {@inheritDoc} - * - * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. - */ - public function getInlineColumnCommentSQL($comment) - { - return '--' . str_replace("\n", "\n--", $comment) . "\n"; - } - - private function getInlineTableCommentSQL(string $comment): string - { - return $this->getInlineColumnCommentSQL($comment); - } - - /** - * {@inheritDoc} - */ - protected function initializeDoctrineTypeMappings() - { - $this->doctrineTypeMapping = [ - 'bigint' => Types\Types::BIGINT, - 'bigserial' => Types\Types::BIGINT, - 'blob' => Types\Types::BLOB, - 'boolean' => Types\Types::BOOLEAN, - 'char' => Types\Types::STRING, - 'clob' => Types\Types::TEXT, - 'date' => Types\Types::DATE_MUTABLE, - 'datetime' => Types\Types::DATETIME_MUTABLE, - 'decimal' => Types\Types::DECIMAL, - 'double' => Types\Types::FLOAT, - 'double precision' => Types\Types::FLOAT, - 'float' => Types\Types::FLOAT, - 'image' => Types\Types::STRING, - 'int' => Types\Types::INTEGER, - 'integer' => Types\Types::INTEGER, - 'longtext' => Types\Types::TEXT, - 'longvarchar' => Types\Types::STRING, - 'mediumint' => Types\Types::INTEGER, - 'mediumtext' => Types\Types::TEXT, - 'ntext' => Types\Types::STRING, - 'numeric' => Types\Types::DECIMAL, - 'nvarchar' => Types\Types::STRING, - 'real' => Types\Types::FLOAT, - 'serial' => Types\Types::INTEGER, - 'smallint' => Types\Types::SMALLINT, - 'text' => Types\Types::TEXT, - 'time' => Types\Types::TIME_MUTABLE, - 'timestamp' => Types\Types::DATETIME_MUTABLE, - 'tinyint' => Types\Types::BOOLEAN, - 'tinytext' => Types\Types::TEXT, - 'varchar' => Types\Types::STRING, - 'varchar2' => Types\Types::STRING, - ]; - } - - /** - * {@inheritDoc} - * - * @deprecated Implement {@see createReservedKeywordsList()} instead. - */ - protected function getReservedKeywordsClass() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' - . ' use SqlitePlatform::createReservedKeywordsList() instead.', - ); - - return Keywords\SQLiteKeywords::class; - } - - /** - * {@inheritDoc} - */ - protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) - { - return []; - } - - /** - * {@inheritDoc} - */ - protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) - { - $table = $diff->getOldTable(); - - if (! $table instanceof Table) { - throw new Exception( - 'Sqlite platform requires for alter table the table diff with reference to original table schema', - ); - } - - $sql = []; - $tableName = $diff->getNewName(); - - if ($tableName === false) { - $tableName = $diff->getName($this); - } - - foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) { - if ($index->isPrimary()) { - continue; - } - - $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - protected function doModifyLimitQuery($query, $limit, $offset) - { - if ($limit === null && $offset > 0) { - return sprintf('%s LIMIT -1 OFFSET %d', $query, $offset); - } - - return parent::doModifyLimitQuery($query, $limit, $offset); - } - - /** - * {@inheritDoc} - */ - public function getBlobTypeDeclarationSQL(array $column) - { - return 'BLOB'; - } - - /** - * {@inheritDoc} - */ - public function getTemporaryTableName($tableName) - { - $tableName = $this->emulateSchemaNamespacing($tableName); - - return $tableName; - } - - /** - * {@inheritDoc} - * - * @deprecated - * - * Sqlite Platform emulates schema by underscoring each dot and generating tables - * into the default database. - * - * This hack is implemented to be able to use SQLite as testdriver when - * using schema supporting databases. - */ - public function canEmulateSchemas() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4805', - 'SqlitePlatform::canEmulateSchemas() is deprecated.', - ); - - return $this->schemaEmulationEnabled; - } - - /** - * {@inheritDoc} - */ - public function getCreateTablesSQL(array $tables): array - { - $sql = []; - - foreach ($tables as $table) { - $sql = array_merge($sql, $this->getCreateTableSQL($table)); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - public function getCreateIndexSQL(Index $index, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this); - } - - $name = $index->getQuotedName($this); - $columns = $index->getColumns(); - - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $name = $schema . '.' . $name; - } - - if (count($columns) === 0) { - throw new InvalidArgumentException(sprintf( - 'Incomplete or invalid index definition %s on table %s', - $name, - $table, - )); - } - - if ($index->isPrimary()) { - return $this->getCreatePrimaryKeySQL($index, $table); - } - - $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; - $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); - - return $query; - } - - /** - * {@inheritDoc} - */ - public function getDropTablesSQL(array $tables): array - { - $sql = []; - - foreach ($tables as $table) { - $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); - } - - return $sql; - } - - /** - * {@inheritDoc} - */ - public function getCreatePrimaryKeySQL(Index $index, $table) - { - throw new Exception('Sqlite platform does not support alter primary key.'); - } - - /** - * {@inheritDoc} - */ - public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) - { - throw new Exception('Sqlite platform does not support alter foreign key.'); - } - - /** - * {@inheritDoc} - */ - public function getDropForeignKeySQL($foreignKey, $table) - { - throw new Exception('Sqlite platform does not support alter foreign key.'); - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getCreateConstraintSQL(Constraint $constraint, $table) - { - throw new Exception('Sqlite platform does not support alter constraint.'); - } - - /** - * {@inheritDoc} - * - * @param int|null $createFlags - * @psalm-param int-mask-of|null $createFlags - */ - public function getCreateTableSQL(Table $table, $createFlags = null) - { - $createFlags = $createFlags ?? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS; - - return parent::getCreateTableSQL($table, $createFlags); - } - - /** - * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. - * - * @param string $table - * @param string|null $database - * - * @return string - */ - public function getListTableForeignKeysSQL($table, $database = null) - { - $table = $this->emulateSchemaNamespacing($table); - - return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table)); - } - - /** - * {@inheritDoc} - */ - public function getAlterTableSQL(TableDiff $diff) - { - $sql = $this->getSimpleAlterTableSQL($diff); - if ($sql !== false) { - return $sql; - } - - $table = $diff->getOldTable(); - - if (! $table instanceof Table) { - throw new Exception( - 'Sqlite platform requires for alter table the table diff with reference to original table schema', - ); - } - - $columns = []; - $oldColumnNames = []; - $newColumnNames = []; - $columnSql = []; - - foreach ($table->getColumns() as $columnName => $column) { - $columnName = strtolower($columnName); - $columns[$columnName] = $column; - $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); - } - - foreach ($diff->getDroppedColumns() as $column) { - if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { - continue; - } - - $columnName = strtolower($column->getName()); - if (! isset($columns[$columnName])) { - continue; - } - - unset( - $columns[$columnName], - $oldColumnNames[$columnName], - $newColumnNames[$columnName], - ); - } - - foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { - continue; - } - - $oldColumnName = strtolower($oldColumnName); - - $columns = $this->replaceColumn( - $table->getName(), - $columns, - $oldColumnName, - $column, - ); - - if (! isset($newColumnNames[$oldColumnName])) { - continue; - } - - $newColumnNames[$oldColumnName] = $column->getQuotedName($this); - } - - foreach ($diff->getModifiedColumns() as $columnDiff) { - if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { - continue; - } - - $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); - - $oldColumnName = strtolower($oldColumn->getName()); - - $columns = $this->replaceColumn( - $table->getName(), - $columns, - $oldColumnName, - $columnDiff->getNewColumn(), - ); - - if (! isset($newColumnNames[$oldColumnName])) { - continue; - } - - $newColumnNames[$oldColumnName] = $columnDiff->getNewColumn()->getQuotedName($this); - } - - foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $columns[strtolower($column->getName())] = $column; - } - - $sql = []; - $tableSql = []; - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - $dataTable = new Table('__temp__' . $table->getName()); - - $newTable = new Table( - $table->getQuotedName($this), - $columns, - $this->getPrimaryIndexInAlteredTable($diff, $table), - [], - $this->getForeignKeysInAlteredTable($diff, $table), - $table->getOptions(), - ); - $newTable->addOption('alter', true); - - $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); - - $sql[] = sprintf( - 'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', - $dataTable->getQuotedName($this), - implode(', ', $oldColumnNames), - $table->getQuotedName($this), - ); - $sql[] = $this->getDropTableSQL($table); - - $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); - $sql[] = sprintf( - 'INSERT INTO %s (%s) SELECT %s FROM %s', - $newTable->getQuotedName($this), - implode(', ', $newColumnNames), - implode(', ', $oldColumnNames), - $dataTable->getQuotedName($this), - ); - $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this)); - - $newName = $diff->getNewName(); - - if ($newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $sql[] = sprintf( - 'ALTER TABLE %s RENAME TO %s', - $newTable->getQuotedName($this), - $newName->getQuotedName($this), - ); - } - - $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** - * Replace the column with the given name with the new column. - * - * @param string $tableName - * @param array $columns - * @param string $columnName - * - * @return array - * - * @throws Exception - */ - private function replaceColumn($tableName, array $columns, $columnName, Column $column): array - { - $keys = array_keys($columns); - $index = array_search($columnName, $keys, true); - - if ($index === false) { - throw SchemaException::columnDoesNotExist($columnName, $tableName); - } - - $values = array_values($columns); - - $keys[$index] = strtolower($column->getName()); - $values[$index] = $column; - - return array_combine($keys, $values); - } - - /** - * @return string[]|false - * - * @throws Exception - */ - private function getSimpleAlterTableSQL(TableDiff $diff) - { - // Suppress changes on integer type autoincrement columns. - foreach ($diff->getModifiedColumns() as $columnDiff) { - $oldColumn = $columnDiff->getOldColumn(); - - if ($oldColumn === null) { - continue; - } - - $newColumn = $columnDiff->getNewColumn(); - - if (! $newColumn->getAutoincrement() || ! $newColumn->getType() instanceof IntegerType) { - continue; - } - - $oldColumnName = $oldColumn->getName(); - - if (! $columnDiff->hasTypeChanged() && $columnDiff->hasUnsignedChanged()) { - unset($diff->changedColumns[$oldColumnName]); - - continue; - } - - $fromColumnType = $oldColumn->getType(); - - if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) { - continue; - } - - unset($diff->changedColumns[$oldColumnName]); - } - - if ( - count($diff->getModifiedColumns()) > 0 - || count($diff->getDroppedColumns()) > 0 - || count($diff->getRenamedColumns()) > 0 - || count($diff->getAddedIndexes()) > 0 - || count($diff->getModifiedIndexes()) > 0 - || count($diff->getDroppedIndexes()) > 0 - || count($diff->getRenamedIndexes()) > 0 - || count($diff->getAddedForeignKeys()) > 0 - || count($diff->getModifiedForeignKeys()) > 0 - || count($diff->getDroppedForeignKeys()) > 0 - ) { - return false; - } - - $table = $diff->getOldTable() ?? $diff->getName($this); - - $sql = []; - $tableSql = []; - $columnSql = []; - - foreach ($diff->getAddedColumns() as $column) { - if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { - continue; - } - - $definition = array_merge([ - 'unique' => null, - 'autoincrement' => null, - 'default' => null, - ], $column->toArray()); - - $type = $definition['type']; - - switch (true) { - case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']: - case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL(): - case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL(): - case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL(): - return false; - } - - $definition['name'] = $column->getQuotedName($this); - if ($type instanceof Types\StringType) { - $definition['length'] ??= 255; - } - - $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' - . $this->getColumnDeclarationSQL($definition['name'], $definition); - } - - if (! $this->onSchemaAlterTable($diff, $tableSql)) { - if ($diff->newName !== false) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - 'Generation of SQL that renames a table using %s is deprecated.' - . ' Use getRenameTableSQL() instead.', - __METHOD__, - ); - - $newTable = new Identifier($diff->newName); - - $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' - . $newTable->getQuotedName($this); - } - } - - return array_merge($sql, $tableSql, $columnSql); - } - - /** @return string[] */ - private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array - { - $columns = []; - - foreach ($fromTable->getColumns() as $columnName => $column) { - $columns[strtolower($columnName)] = $column->getName(); - } - - foreach ($diff->getDroppedColumns() as $column) { - $columnName = strtolower($column->getName()); - if (! isset($columns[$columnName])) { - continue; - } - - unset($columns[$columnName]); - } - - foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { - $columnName = $column->getName(); - $columns[strtolower($oldColumnName)] = $columnName; - $columns[strtolower($columnName)] = $columnName; - } - - foreach ($diff->getModifiedColumns() as $columnDiff) { - $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); - - $oldColumnName = $oldColumn->getName(); - $newColumnName = $columnDiff->getNewColumn()->getName(); - $columns[strtolower($oldColumnName)] = $newColumnName; - $columns[strtolower($newColumnName)] = $newColumnName; - } - - foreach ($diff->getAddedColumns() as $column) { - $columnName = $column->getName(); - $columns[strtolower($columnName)] = $columnName; - } - - return $columns; - } - - /** @return Index[] */ - private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): array - { - $indexes = $fromTable->getIndexes(); - $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); - - foreach ($indexes as $key => $index) { - foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) { - if (strtolower($key) !== strtolower($oldIndexName)) { - continue; - } - - unset($indexes[$key]); - } - - $changed = false; - $indexColumns = []; - foreach ($index->getColumns() as $columnName) { - $normalizedColumnName = strtolower($columnName); - if (! isset($columnNames[$normalizedColumnName])) { - unset($indexes[$key]); - continue 2; - } - - $indexColumns[] = $columnNames[$normalizedColumnName]; - if ($columnName === $columnNames[$normalizedColumnName]) { - continue; - } - - $changed = true; - } - - if (! $changed) { - continue; - } - - $indexes[$key] = new Index( - $index->getName(), - $indexColumns, - $index->isUnique(), - $index->isPrimary(), - $index->getFlags(), - ); - } - - foreach ($diff->getDroppedIndexes() as $index) { - $indexName = strtolower($index->getName()); - if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) { - continue; - } - - unset($indexes[$indexName]); - } - - foreach ( - array_merge( - $diff->getModifiedIndexes(), - $diff->getAddedIndexes(), - $diff->getRenamedIndexes(), - ) as $index - ) { - $indexName = strtolower($index->getName()); - if (strlen($indexName) > 0) { - $indexes[$indexName] = $index; - } else { - $indexes[] = $index; - } - } - - return $indexes; - } - - /** @return ForeignKeyConstraint[] */ - private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array - { - $foreignKeys = $fromTable->getForeignKeys(); - $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); - - foreach ($foreignKeys as $key => $constraint) { - $changed = false; - $localColumns = []; - foreach ($constraint->getLocalColumns() as $columnName) { - $normalizedColumnName = strtolower($columnName); - if (! isset($columnNames[$normalizedColumnName])) { - unset($foreignKeys[$key]); - continue 2; - } - - $localColumns[] = $columnNames[$normalizedColumnName]; - if ($columnName === $columnNames[$normalizedColumnName]) { - continue; - } - - $changed = true; - } - - if (! $changed) { - continue; - } - - $foreignKeys[$key] = new ForeignKeyConstraint( - $localColumns, - $constraint->getForeignTableName(), - $constraint->getForeignColumns(), - $constraint->getName(), - $constraint->getOptions(), - ); - } - - foreach ($diff->getDroppedForeignKeys() as $constraint) { - if (! $constraint instanceof ForeignKeyConstraint) { - $constraint = new Identifier($constraint); - } - - $constraintName = strtolower($constraint->getName()); - if (strlen($constraintName) === 0 || ! isset($foreignKeys[$constraintName])) { - continue; - } - - unset($foreignKeys[$constraintName]); - } - - foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) { - $constraintName = strtolower($constraint->getName()); - if (strlen($constraintName) > 0) { - $foreignKeys[$constraintName] = $constraint; - } else { - $foreignKeys[] = $constraint; - } - } - - return $foreignKeys; - } - - /** @return Index[] */ - private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array - { - $primaryIndex = []; - - foreach ($this->getIndexesInAlteredTable($diff, $fromTable) as $index) { - if (! $index->isPrimary()) { - continue; - } - - $primaryIndex = [$index->getName() => $index]; - } - - return $primaryIndex; - } - - public function createSchemaManager(Connection $connection): SqliteSchemaManager - { - return new SqliteSchemaManager($connection, $this); - } -} diff --git a/doctrine/dbal/src/Platforms/TrimMode.php b/doctrine/dbal/src/Platforms/TrimMode.php index 01356c0d3..31c237543 100644 --- a/doctrine/dbal/src/Platforms/TrimMode.php +++ b/doctrine/dbal/src/Platforms/TrimMode.php @@ -4,18 +4,10 @@ namespace Doctrine\DBAL\Platforms; -final class TrimMode +enum TrimMode { - public const UNSPECIFIED = 0; - - public const LEADING = 1; - - public const TRAILING = 2; - - public const BOTH = 3; - - /** @codeCoverageIgnore */ - private function __construct() - { - } + case UNSPECIFIED; + case LEADING; + case TRAILING; + case BOTH; } diff --git a/doctrine/dbal/src/Portability/Connection.php b/doctrine/dbal/src/Portability/Connection.php index fc1bdd9ad..4a821d830 100644 --- a/doctrine/dbal/src/Portability/Connection.php +++ b/doctrine/dbal/src/Portability/Connection.php @@ -1,11 +1,11 @@ converter = $converter; } - public function prepare(string $sql): DriverStatement + public function prepare(string $sql): Statement { return new Statement( parent::prepare($sql), @@ -35,7 +31,7 @@ public function prepare(string $sql): DriverStatement ); } - public function query(string $sql): DriverResult + public function query(string $sql): Result { return new Result( parent::query($sql), diff --git a/doctrine/dbal/src/Portability/Converter.php b/doctrine/dbal/src/Portability/Converter.php index d0503977b..09a4712b7 100644 --- a/doctrine/dbal/src/Portability/Converter.php +++ b/doctrine/dbal/src/Portability/Converter.php @@ -4,6 +4,8 @@ namespace Doctrine\DBAL\Portability; +use Closure; + use function array_change_key_case; use function array_map; use function array_reduce; @@ -18,23 +20,12 @@ final class Converter public const CASE_LOWER = CASE_LOWER; public const CASE_UPPER = CASE_UPPER; - /** @var callable */ - private $convertNumeric; - - /** @var callable */ - private $convertAssociative; - - /** @var callable */ - private $convertOne; - - /** @var callable */ - private $convertAllNumeric; - - /** @var callable */ - private $convertAllAssociative; - - /** @var callable */ - private $convertFirstColumn; + private readonly Closure $convertNumeric; + private readonly Closure $convertAssociative; + private readonly Closure $convertOne; + private readonly Closure $convertAllNumeric; + private readonly Closure $convertAllAssociative; + private readonly Closure $convertFirstColumn; /** * @param bool $convertEmptyStringToNull Whether each empty string should @@ -50,13 +41,13 @@ public function __construct(bool $convertEmptyStringToNull, bool $rightTrimStrin $convertNumeric = $this->createConvertRow($convertValue, null); $convertAssociative = $this->createConvertRow($convertValue, $case); - $this->convertNumeric = $this->createConvert($convertNumeric, [self::class, 'id']); - $this->convertAssociative = $this->createConvert($convertAssociative, [self::class, 'id']); - $this->convertOne = $this->createConvert($convertValue, [self::class, 'id']); + $this->convertNumeric = $this->createConvert($convertNumeric); + $this->convertAssociative = $this->createConvert($convertAssociative); + $this->convertOne = $this->createConvert($convertValue); - $this->convertAllNumeric = $this->createConvertAll($convertNumeric, [self::class, 'id']); - $this->convertAllAssociative = $this->createConvertAll($convertAssociative, [self::class, 'id']); - $this->convertFirstColumn = $this->createConvertAll($convertValue, [self::class, 'id']); + $this->convertAllNumeric = $this->createConvertAll($convertNumeric); + $this->convertAllAssociative = $this->createConvertAll($convertAssociative); + $this->convertFirstColumn = $this->createConvertAll($convertValue); } /** @@ -64,7 +55,7 @@ public function __construct(bool $convertEmptyStringToNull, bool $rightTrimStrin * * @return list|false */ - public function convertNumeric($row) + public function convertNumeric(array|false $row): array|false { return ($this->convertNumeric)($row); } @@ -74,17 +65,12 @@ public function convertNumeric($row) * * @return array|false */ - public function convertAssociative($row) + public function convertAssociative(array|false $row): array|false { return ($this->convertAssociative)($row); } - /** - * @param mixed|false $value - * - * @return mixed|false - */ - public function convertOne($value) + public function convertOne(mixed $value): mixed { return ($this->convertOne)($value); } @@ -126,7 +112,7 @@ public function convertFirstColumn(array $data): array * * @template T */ - private static function id($value) + private static function id(mixed $value): mixed { return $value; } @@ -138,7 +124,7 @@ private static function id($value) * * @template T */ - private static function convertEmptyStringToNull($value) + private static function convertEmptyStringToNull(mixed $value): mixed { if ($value === '') { return null; @@ -155,7 +141,7 @@ private static function convertEmptyStringToNull($value) * * @template T */ - private static function rightTrimString($value) + private static function rightTrimString(mixed $value): mixed { if (! is_string($value)) { return $value; @@ -170,18 +156,18 @@ private static function rightTrimString($value) * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL * @param bool $rightTrimString Whether each string should right-trimmed * - * @return callable|null The resulting function or NULL if no conversion is needed + * @return Closure|null The resulting function or NULL if no conversion is needed */ - private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?callable + private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?Closure { $functions = []; if ($convertEmptyStringToNull) { - $functions[] = [self::class, 'convertEmptyStringToNull']; + $functions[] = self::convertEmptyStringToNull(...); } if ($rightTrimString) { - $functions[] = [self::class, 'rightTrimString']; + $functions[] = self::rightTrimString(...); } return $this->compose(...$functions); @@ -190,12 +176,12 @@ private function createConvertValue(bool $convertEmptyStringToNull, bool $rightT /** * Creates a function that will convert each array-row retrieved from the database * - * @param callable|null $function The function that will convert each value + * @param Closure|null $function The function that will convert each value * @param self::CASE_LOWER|self::CASE_UPPER|null $case Column name case * - * @return callable|null The resulting function or NULL if no conversion is needed + * @return Closure|null The resulting function or NULL if no conversion is needed */ - private function createConvertRow(?callable $function, ?int $case): ?callable + private function createConvertRow(?Closure $function, ?int $case): ?Closure { $functions = []; @@ -204,9 +190,7 @@ private function createConvertRow(?callable $function, ?int $case): ?callable } if ($case !== null) { - $functions[] = static function (array $row) use ($case): array { - return array_change_key_case($row, $case); - }; + $functions[] = static fn (array $row): array => array_change_key_case($row, $case); } return $this->compose(...$functions); @@ -216,13 +200,12 @@ private function createConvertRow(?callable $function, ?int $case): ?callable * Creates a function that will be applied to the return value of Statement::fetch*() * or an identity function if no conversion is needed * - * @param callable|null $function The function that will convert each tow - * @param callable $id Identity function + * @param Closure|null $function The function that will convert each tow */ - private function createConvert(?callable $function, callable $id): callable + private function createConvert(?Closure $function): Closure { if ($function === null) { - return $id; + return self::id(...); } return /** @@ -232,7 +215,7 @@ private function createConvert(?callable $function, callable $id): callable * * @template T */ - static function ($value) use ($function) { + static function (mixed $value) use ($function): mixed { if ($value === false) { return false; } @@ -245,13 +228,12 @@ static function ($value) use ($function) { * Creates a function that will be applied to the return value of Statement::fetchAll*() * or an identity function if no transformation is required * - * @param callable|null $function The function that will transform each value - * @param callable $id Identity function + * @param Closure|null $function The function that will transform each value */ - private function createConvertAll(?callable $function, callable $id): callable + private function createConvertAll(?Closure $function): Closure { if ($function === null) { - return $id; + return self::id(...); } return $this->createMapper($function); @@ -260,27 +242,27 @@ private function createConvertAll(?callable $function, callable $id): callable /** * Creates a function that maps each value of the array using the given function * - * @param callable $function The function that maps each value of the array + * @param Closure $function The function that maps each value of the array + * + * @return Closure(array):array */ - private function createMapper(callable $function): callable + private function createMapper(Closure $function): Closure { - return static function (array $array) use ($function): array { - return array_map($function, $array); - }; + return static fn (array $array): array => array_map($function, $array); } /** * Creates a composition of the given set of functions * - * @param callable(T):T ...$functions The functions to compose + * @param Closure(T):T ...$functions The functions to compose * - * @return callable(T):T|null + * @return Closure(T):T|null * * @template T */ - private function compose(callable ...$functions): ?callable + private function compose(Closure ...$functions): ?Closure { - return array_reduce($functions, static function (?callable $carry, callable $item): callable { + return array_reduce($functions, static function (?Closure $carry, Closure $item): Closure { if ($carry === null) { return $item; } @@ -292,9 +274,7 @@ private function compose(callable ...$functions): ?callable * * @template T */ - static function ($value) use ($carry, $item) { - return $item($carry($value)); - }; + static fn (mixed $value): mixed => $item($carry($value)); }); } } diff --git a/doctrine/dbal/src/Portability/Driver.php b/doctrine/dbal/src/Portability/Driver.php index a5ac9679a..bb67c258b 100644 --- a/doctrine/dbal/src/Portability/Driver.php +++ b/doctrine/dbal/src/Portability/Driver.php @@ -1,35 +1,27 @@ mode = $mode; - $this->case = $case; } /** @@ -37,34 +29,28 @@ public function __construct(DriverInterface $driver, int $mode, int $case) */ public function connect( #[SensitiveParameter] - array $params - ) { + array $params, + ): ConnectionInterface { $connection = parent::connect($params); $portability = (new OptimizeFlags())( - $this->getDatabasePlatform(), + $this->getDatabasePlatform($connection), $this->mode, ); $case = null; - if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) { - $nativeConnection = null; - if (method_exists($connection, 'getNativeConnection')) { - try { - $nativeConnection = $connection->getNativeConnection(); - } catch (LogicException $e) { - } - } + if ($this->case !== null && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) { + $nativeConnection = $connection->getNativeConnection(); + + $case = match ($this->case) { + ColumnCase::LOWER => CASE_LOWER, + ColumnCase::UPPER => CASE_UPPER, + }; if ($nativeConnection instanceof PDO) { $portability &= ~Connection::PORTABILITY_FIX_CASE; - $nativeConnection->setAttribute( - PDO::ATTR_CASE, - $this->case === ColumnCase::LOWER ? PDO::CASE_LOWER : PDO::CASE_UPPER, - ); - } else { - $case = $this->case === ColumnCase::LOWER ? Converter::CASE_LOWER : Converter::CASE_UPPER; + $nativeConnection->setAttribute(PDO::ATTR_CASE, $case); } } diff --git a/doctrine/dbal/src/Portability/Middleware.php b/doctrine/dbal/src/Portability/Middleware.php index fae2c5567..b97897c97 100644 --- a/doctrine/dbal/src/Portability/Middleware.php +++ b/doctrine/dbal/src/Portability/Middleware.php @@ -10,21 +10,8 @@ final class Middleware implements MiddlewareInterface { - private int $mode; - - /** @var 0|ColumnCase::LOWER|ColumnCase::UPPER */ - private int $case; - - /** - * @param 0|ColumnCase::LOWER|ColumnCase::UPPER $case Determines how the column case will be treated. - * 0: The case will be left as is in the database. - * {@see ColumnCase::LOWER}: The case will be lowercased. - * {@see ColumnCase::UPPER}: The case will be uppercased. - */ - public function __construct(int $mode, int $case) + public function __construct(private readonly int $mode, private readonly ?ColumnCase $case) { - $this->mode = $mode; - $this->case = $case; } public function wrap(DriverInterface $driver): DriverInterface diff --git a/doctrine/dbal/src/Portability/OptimizeFlags.php b/doctrine/dbal/src/Portability/OptimizeFlags.php index 884a936c7..c985d4bfd 100644 --- a/doctrine/dbal/src/Portability/OptimizeFlags.php +++ b/doctrine/dbal/src/Portability/OptimizeFlags.php @@ -8,7 +8,7 @@ use Doctrine\DBAL\Platforms\DB2Platform; use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Platforms\SQLServerPlatform; final class OptimizeFlags @@ -23,7 +23,7 @@ final class OptimizeFlags DB2Platform::class => 0, OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, PostgreSQLPlatform::class => 0, - SqlitePlatform::class => 0, + SQLitePlatform::class => 0, SQLServerPlatform::class => 0, ]; diff --git a/doctrine/dbal/src/Portability/Result.php b/doctrine/dbal/src/Portability/Result.php index da1eca985..d158683c1 100644 --- a/doctrine/dbal/src/Portability/Result.php +++ b/doctrine/dbal/src/Portability/Result.php @@ -9,40 +9,27 @@ final class Result extends AbstractResultMiddleware { - private Converter $converter; - /** @internal The result can be only instantiated by the portability connection or statement. */ - public function __construct(ResultInterface $result, Converter $converter) + public function __construct(ResultInterface $result, private readonly Converter $converter) { parent::__construct($result); - - $this->converter = $converter; } - /** - * {@inheritDoc} - */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->converter->convertNumeric( parent::fetchNumeric(), ); } - /** - * {@inheritDoc} - */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->converter->convertAssociative( parent::fetchAssociative(), ); } - /** - * {@inheritDoc} - */ - public function fetchOne() + public function fetchOne(): mixed { return $this->converter->convertOne( parent::fetchOne(), diff --git a/doctrine/dbal/src/Portability/Statement.php b/doctrine/dbal/src/Portability/Statement.php index 8fcd79d4a..de0c76fc4 100644 --- a/doctrine/dbal/src/Portability/Statement.php +++ b/doctrine/dbal/src/Portability/Statement.php @@ -1,5 +1,7 @@ Statement and applies portability measures. */ - public function __construct(DriverStatement $stmt, Converter $converter) + public function __construct(DriverStatement $stmt, private readonly Converter $converter) { parent::__construct($stmt); - - $this->converter = $converter; } - /** - * {@inheritDoc} - */ - public function execute($params = null): ResultInterface + public function execute(): ResultInterface { return new Result( - parent::execute($params), + parent::execute(), $this->converter, ); } diff --git a/doctrine/dbal/src/Query.php b/doctrine/dbal/src/Query.php index bfc9b14e2..b673ba889 100644 --- a/doctrine/dbal/src/Query.php +++ b/doctrine/dbal/src/Query.php @@ -4,45 +4,25 @@ namespace Doctrine\DBAL; -use Doctrine\DBAL\Types\Type; - /** * An SQL query together with its bound parameters. * * @psalm-immutable + * @psalm-import-type WrapperParameterType from Connection */ final class Query { /** - * The SQL query. - */ - private string $sql; - - /** - * The parameters bound to the query. - * - * @var array - */ - private array $params; - - /** - * The types of the parameters bound to the query. - * - * @var array - */ - private array $types; - - /** - * @param array $params - * @param array $types + * @param array $params + * @psalm-param array $types * * @psalm-suppress ImpurePropertyAssignment */ - public function __construct(string $sql, array $params, array $types) - { - $this->sql = $sql; - $this->params = $params; - $this->types = $types; + public function __construct( + private readonly string $sql, + private readonly array $params, + private readonly array $types, + ) { } public function getSQL(): string @@ -56,7 +36,7 @@ public function getParams(): array return $this->params; } - /** @return array */ + /** @psalm-return array */ public function getTypes(): array { return $this->types; diff --git a/doctrine/dbal/src/Query/Exception/NonUniqueAlias.php b/doctrine/dbal/src/Query/Exception/NonUniqueAlias.php new file mode 100644 index 000000000..8aab6026f --- /dev/null +++ b/doctrine/dbal/src/Query/Exception/NonUniqueAlias.php @@ -0,0 +1,27 @@ +type = $type; - - $this->addMultiple($parts); - - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3864', - 'Do not use CompositeExpression constructor directly, use static and() and or() factory methods.', - ); - } - - /** - * @param self|string $part - * @param self|string ...$parts - */ - public static function and($part, ...$parts): self - { - return new self(self::TYPE_AND, array_merge([$part], $parts)); - } - - /** - * @param self|string $part - * @param self|string ...$parts + * @var array */ - public static function or($part, ...$parts): self - { - return new self(self::TYPE_OR, array_merge([$part], $parts)); + private array $parts; + + /** @internal Use the and() / or() factory methods. */ + public function __construct( + private readonly string $type, + self|string $part, + self|string ...$parts, + ) { + $this->parts = array_merge([$part], array_values($parts)); } - /** - * Adds multiple parts to composite expression. - * - * @deprecated This class will be made immutable. Use with() instead. - * - * @param self[]|string[] $parts - * - * @return CompositeExpression - */ - public function addMultiple(array $parts = []) + public static function and(self|string $part, self|string ...$parts): self { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3844', - 'CompositeExpression::addMultiple() is deprecated, use CompositeExpression::with() instead.', - ); - - foreach ($parts as $part) { - $this->add($part); - } - - return $this; + return new self(self::TYPE_AND, $part, ...$parts); } - /** - * Adds an expression to composite expression. - * - * @deprecated This class will be made immutable. Use with() instead. - * - * @param mixed $part - * - * @return CompositeExpression - */ - public function add($part) + public static function or(self|string $part, self|string ...$parts): self { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3844', - 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.', - ); - - if ($part === null) { - return $this; - } - - if ($part instanceof self && count($part) === 0) { - return $this; - } - - $this->parts[] = $part; - - return $this; + return new self(self::TYPE_OR, $part, ...$parts); } /** * Returns a new CompositeExpression with the given parts added. - * - * @param self|string $part - * @param self|string ...$parts */ - public function with($part, ...$parts): self + public function with(self|string $part, self|string ...$parts): self { $that = clone $this; - $that->parts = array_merge($that->parts, [$part], $parts); + $that->parts = array_merge($that->parts, [$part], array_values($parts)); return $that; } @@ -148,21 +69,17 @@ public function with($part, ...$parts): self /** * Retrieves the amount of expressions on composite expression. * - * @return int * @psalm-return int<0, max> */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { return count($this->parts); } /** * Retrieves the string representation of this composite expression. - * - * @return string */ - public function __toString() + public function __toString(): string { if ($this->count() === 1) { return (string) $this->parts[0]; @@ -173,10 +90,8 @@ public function __toString() /** * Returns the type of this composite expression (AND/OR). - * - * @return string */ - public function getType() + public function getType(): string { return $this->type; } diff --git a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php index d532b2894..14942b2f9 100644 --- a/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php +++ b/doctrine/dbal/src/Query/Expression/ExpressionBuilder.php @@ -1,13 +1,11 @@ '; - public const LT = '<'; - public const LTE = '<='; - public const GT = '>'; - public const GTE = '>='; - - /** - * The DBAL Connection. - */ - private Connection $connection; + final public const EQ = '='; + final public const NEQ = '<>'; + final public const LT = '<'; + final public const LTE = '<='; + final public const GT = '>'; + final public const GTE = '>='; /** * Initializes a new ExpressionBuilder. * * @param Connection $connection The DBAL Connection. */ - public function __construct(Connection $connection) + public function __construct(private readonly Connection $connection) { - $this->connection = $connection; } /** * Creates a conjunction of the given expressions. - * - * @param string|CompositeExpression $expression - * @param string|CompositeExpression ...$expressions */ - public function and($expression, ...$expressions): CompositeExpression - { + public function and( + string|CompositeExpression $expression, + string|CompositeExpression ...$expressions, + ): CompositeExpression { return CompositeExpression::and($expression, ...$expressions); } /** * Creates a disjunction of the given expressions. - * - * @param string|CompositeExpression $expression - * @param string|CompositeExpression ...$expressions */ - public function or($expression, ...$expressions): CompositeExpression - { + public function or( + string|CompositeExpression $expression, + string|CompositeExpression ...$expressions, + ): CompositeExpression { return CompositeExpression::or($expression, ...$expressions); } - /** - * @deprecated Use `and()` instead. - * - * @param mixed $x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. - * - * @return CompositeExpression - */ - public function andX($x = null) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3851', - 'ExpressionBuilder::andX() is deprecated, use ExpressionBuilder::and() instead.', - ); - - return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); - } - - /** - * @deprecated Use `or()` instead. - * - * @param mixed $x Optional clause. Defaults = null, but requires - * at least one defined when converting to string. - * - * @return CompositeExpression - */ - public function orX($x = null) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/3851', - 'ExpressionBuilder::orX() is deprecated, use ExpressionBuilder::or() instead.', - ); - - return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); - } - /** * Creates a comparison expression. * - * @param mixed $x The left expression. - * @param string $operator One of the ExpressionBuilder::* constants. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $operator The comparison operator. + * @param string $y The right expression. */ - public function comparison($x, $operator, $y) + public function comparison(string $x, string $operator, string $y): string { return $x . ' ' . $operator . ' ' . $y; } @@ -122,12 +72,10 @@ public function comparison($x, $operator, $y) * // u.id = ? * $expr->eq('u.id', '?'); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function eq($x, $y) + public function eq(string $x, string $y): string { return $this->comparison($x, self::EQ, $y); } @@ -141,12 +89,10 @@ public function eq($x, $y) * // u.id <> 1 * $q->where($q->expr()->neq('u.id', '1')); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function neq($x, $y) + public function neq(string $x, string $y): string { return $this->comparison($x, self::NEQ, $y); } @@ -160,12 +106,10 @@ public function neq($x, $y) * // u.id < ? * $q->where($q->expr()->lt('u.id', '?')); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function lt($x, $y) + public function lt(string $x, string $y): string { return $this->comparison($x, self::LT, $y); } @@ -179,12 +123,10 @@ public function lt($x, $y) * // u.id <= ? * $q->where($q->expr()->lte('u.id', '?')); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function lte($x, $y) + public function lte(string $x, string $y): string { return $this->comparison($x, self::LTE, $y); } @@ -198,12 +140,10 @@ public function lte($x, $y) * // u.id > ? * $q->where($q->expr()->gt('u.id', '?')); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function gt($x, $y) + public function gt(string $x, string $y): string { return $this->comparison($x, self::GT, $y); } @@ -217,12 +157,10 @@ public function gt($x, $y) * // u.id >= ? * $q->where($q->expr()->gte('u.id', '?')); * - * @param mixed $x The left expression. - * @param mixed $y The right expression. - * - * @return string + * @param string $x The left expression. + * @param string $y The right expression. */ - public function gte($x, $y) + public function gte(string $x, string $y): string { return $this->comparison($x, self::GTE, $y); } @@ -231,10 +169,8 @@ public function gte($x, $y) * Creates an IS NULL expression with the given arguments. * * @param string $x The expression to be restricted by IS NULL. - * - * @return string */ - public function isNull($x) + public function isNull(string $x): string { return $x . ' IS NULL'; } @@ -243,40 +179,34 @@ public function isNull($x) * Creates an IS NOT NULL expression with the given arguments. * * @param string $x The expression to be restricted by IS NOT NULL. - * - * @return string */ - public function isNotNull($x) + public function isNotNull(string $x): string { return $x . ' IS NOT NULL'; } /** - * Creates a LIKE() comparison expression with the given arguments. + * Creates a LIKE comparison expression. * - * @param string $x The expression to be inspected by the LIKE comparison - * @param mixed $y The pattern to compare against - * - * @return string + * @param string $expression The expression to be inspected by the LIKE comparison + * @param string $pattern The pattern to compare against */ - public function like($x, $y/*, ?string $escapeChar = null */) + public function like(string $expression, string $pattern, ?string $escapeChar = null): string { - return $this->comparison($x, 'LIKE', $y) . - (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : ''); + return $this->comparison($expression, 'LIKE', $pattern) . + ($escapeChar !== null ? sprintf(' ESCAPE %s', $escapeChar) : ''); } /** - * Creates a NOT LIKE() comparison expression with the given arguments. - * - * @param string $x The expression to be inspected by the NOT LIKE comparison - * @param mixed $y The pattern to compare against + * Creates a NOT LIKE comparison expression * - * @return string + * @param string $expression The expression to be inspected by the NOT LIKE comparison + * @param string $pattern The pattern to compare against */ - public function notLike($x, $y/*, ?string $escapeChar = null */) + public function notLike(string $expression, string $pattern, ?string $escapeChar = null): string { - return $this->comparison($x, 'NOT LIKE', $y) . - (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : ''); + return $this->comparison($expression, 'NOT LIKE', $pattern) . + ($escapeChar !== null ? sprintf(' ESCAPE %s', $escapeChar) : ''); } /** @@ -284,10 +214,8 @@ public function notLike($x, $y/*, ?string $escapeChar = null */) * * @param string $x The SQL expression to be matched against the set. * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. - * - * @return string */ - public function in($x, $y) + public function in(string $x, string|array $y): string { return $this->comparison($x, 'IN', '(' . implode(', ', (array) $y) . ')'); } @@ -297,27 +225,20 @@ public function in($x, $y) * * @param string $x The SQL expression to be matched against the set. * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. - * - * @return string */ - public function notIn($x, $y) + public function notIn(string $x, string|array $y): string { return $this->comparison($x, 'NOT IN', '(' . implode(', ', (array) $y) . ')'); } /** - * Builds an SQL literal from a given input parameter. + * Creates an SQL literal expression from the string. * * The usage of this method is discouraged. Use prepared statements * or {@see AbstractPlatform::quoteStringLiteral()} instead. - * - * @param mixed $input The parameter to be quoted. - * @param int|null $type The type of the parameter. - * - * @return string */ - public function literal($input, $type = null) + public function literal(string $input): string { - return $this->connection->quote($input, $type); + return $this->connection->quote($input); } } diff --git a/doctrine/dbal/src/Query/ForUpdate.php b/doctrine/dbal/src/Query/ForUpdate.php index fe54df909..24a853e92 100644 --- a/doctrine/dbal/src/Query/ForUpdate.php +++ b/doctrine/dbal/src/Query/ForUpdate.php @@ -4,17 +4,17 @@ namespace Doctrine\DBAL\Query; +use Doctrine\DBAL\Query\ForUpdate\ConflictResolutionMode; + /** @internal */ final class ForUpdate { - private int $conflictResolutionMode; - - public function __construct(int $conflictResolutionMode) - { - $this->conflictResolutionMode = $conflictResolutionMode; + public function __construct( + private readonly ConflictResolutionMode $conflictResolutionMode, + ) { } - public function getConflictResolutionMode(): int + public function getConflictResolutionMode(): ConflictResolutionMode { return $this->conflictResolutionMode; } diff --git a/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php b/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php index f968f7b94..f45f774bd 100644 --- a/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php +++ b/doctrine/dbal/src/Query/ForUpdate/ConflictResolutionMode.php @@ -4,24 +4,15 @@ namespace Doctrine\DBAL\Query\ForUpdate; -final class ConflictResolutionMode +enum ConflictResolutionMode { /** * Wait for the row to be unlocked */ - public const ORDINARY = 0; + case ORDINARY; /** * Skip the row if it is locked */ - public const SKIP_LOCKED = 1; - - /** - * This class cannot be instantiated. - * - * @codeCoverageIgnore - */ - private function __construct() - { - } + case SKIP_LOCKED; } diff --git a/doctrine/dbal/src/Query/From.php b/doctrine/dbal/src/Query/From.php new file mode 100644 index 000000000..71ff50dfb --- /dev/null +++ b/doctrine/dbal/src/Query/From.php @@ -0,0 +1,15 @@ +maxResults = $maxResults; - $this->firstResult = $firstResult; + public function __construct( + private readonly ?int $maxResults, + private readonly int $firstResult, + ) { } public function isDefined(): bool diff --git a/doctrine/dbal/src/Query/QueryBuilder.php b/doctrine/dbal/src/Query/QueryBuilder.php index f099e756b..9f4eb5a30 100644 --- a/doctrine/dbal/src/Query/QueryBuilder.php +++ b/doctrine/dbal/src/Query/QueryBuilder.php @@ -1,34 +1,31 @@ |array + */ + private array $params = []; - /** @deprecated */ - public const INSERT = 3; + /** + * The parameter type map of this query. + * + * @psalm-var WrapperParameterTypeArray + */ + private array $types = []; - /** @deprecated */ - public const STATE_DIRTY = 0; + /** + * The type of query this is. Can be select, update or delete. + */ + private QueryType $type = QueryType::SELECT; - /** @deprecated */ - public const STATE_CLEAN = 1; + /** + * The index of the first result to retrieve. + */ + private int $firstResult = 0; /** - * The DBAL Connection. + * The maximum number of results to retrieve or NULL to retrieve all results. */ - private Connection $connection; + private ?int $maxResults = null; - /* - * The default values of SQL parts collection + /** + * The counter of bound parameters used with {@see bindValue). + * + * @var int<0, max> */ - private const SQL_PARTS_DEFAULTS = [ - 'select' => [], - 'distinct' => false, - 'from' => [], - 'join' => [], - 'set' => [], - 'where' => null, - 'groupBy' => [], - 'having' => null, - 'orderBy' => [], - 'values' => [], - 'for_update' => null, - ]; + private int $boundCounter = 0; /** - * The array of SQL parts collected. + * The SELECT parts of the query. * - * @var mixed[] + * @var string[] */ - private array $sqlParts = self::SQL_PARTS_DEFAULTS; + private array $select = []; /** - * The complete SQL string for this query. + * Whether this is a SELECT DISTINCT query. */ - private ?string $sql = null; + private bool $distinct = false; /** - * The query parameters. + * The FROM parts of a SELECT query. * - * @var list|array + * @var From[] */ - private $params = []; + private array $from = []; /** - * The parameter type map of this query. + * The table name for an INSERT, UPDATE or DELETE query. + */ + private ?string $table = null; + + /** + * The list of joins, indexed by from alias. * - * @var array|array + * @var array */ - private array $paramTypes = []; + private array $join = []; /** - * The type of query this is. Can be select, update or delete. + * The SET parts of an UPDATE query. * - * @psalm-var self::SELECT|self::DELETE|self::UPDATE|self::INSERT + * @var string[] */ - private int $type = self::SELECT; + private array $set = []; /** - * The state of the query object. Can be dirty or clean. + * The WHERE part of a SELECT, UPDATE or DELETE query. + */ + private string|CompositeExpression|null $where = null; + + /** + * The GROUP BY part of a SELECT query. * - * @psalm-var self::STATE_* + * @var string[] */ - private int $state = self::STATE_CLEAN; + private array $groupBy = []; /** - * The index of the first result to retrieve. + * The HAVING part of a SELECT query. */ - private int $firstResult = 0; + private string|CompositeExpression|null $having = null; /** - * The maximum number of results to retrieve or NULL to retrieve all results. + * The ORDER BY parts of a SELECT query. + * + * @var string[] */ - private ?int $maxResults = null; + private array $orderBy = []; + + private ?ForUpdate $forUpdate = null; /** - * The counter of bound parameters used with {@see bindValue). + * The values of an INSERT query. + * + * @var array */ - private int $boundCounter = 0; + private array $values = []; /** * The query cache profile used for caching results. @@ -149,9 +163,8 @@ class QueryBuilder * * @param Connection $connection The DBAL Connection. */ - public function __construct(Connection $connection) + public function __construct(private readonly Connection $connection) { - $this->connection = $connection; } /** @@ -167,69 +180,10 @@ public function __construct(Connection $connection) * * For more complex expression construction, consider storing the expression * builder object in a local variable. - * - * @return ExpressionBuilder - */ - public function expr() - { - return $this->connection->getExpressionBuilder(); - } - - /** - * Gets the type of the currently built query. - * - * @deprecated If necessary, track the type of the query being built outside of the builder. - * - * @return int - */ - public function getType() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5551', - 'Relying on the type of the query being built is deprecated.' - . ' If necessary, track the type of the query being built outside of the builder.', - ); - - return $this->type; - } - - /** - * Gets the associated DBAL Connection for this query builder. - * - * @deprecated Use the connection used to instantiate the builder instead. - * - * @return Connection - */ - public function getConnection() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5780', - '%s is deprecated. Use the connection used to instantiate the builder instead.', - __METHOD__, - ); - - return $this->connection; - } - - /** - * Gets the state of this query builder instance. - * - * @deprecated The builder state is an internal concern. - * - * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. - * @psalm-return self::STATE_* */ - public function getState() + public function expr(): ExpressionBuilder { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5551', - 'Relying on the query builder state is deprecated as it is an internal concern.', - ); - - return $this->state; + return $this->connection->createExpressionBuilder(); } /** @@ -240,7 +194,7 @@ public function getState() * * @throws Exception */ - public function fetchAssociative() + public function fetchAssociative(): array|false { return $this->executeQuery()->fetchAssociative(); } @@ -253,7 +207,7 @@ public function fetchAssociative() * * @throws Exception */ - public function fetchNumeric() + public function fetchNumeric(): array|false { return $this->executeQuery()->fetchNumeric(); } @@ -266,7 +220,7 @@ public function fetchNumeric() * * @throws Exception */ - public function fetchOne() + public function fetchOne(): mixed { return $this->executeQuery()->fetchOne(); } @@ -344,7 +298,7 @@ public function executeQuery(): Result return $this->connection->executeQuery( $this->getSQL(), $this->params, - $this->paramTypes, + $this->types, $this->resultCacheProfile, ); } @@ -354,43 +308,13 @@ public function executeQuery(): Result * * Should be used for INSERT, UPDATE and DELETE * - * @return int The number of affected rows. - * - * @throws Exception - */ - public function executeStatement(): int - { - return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); - } - - /** - * Executes this query using the bound parameters and their types. - * - * @deprecated Use {@see executeQuery()} or {@see executeStatement()} instead. - * - * @return Result|int|string + * @return int|numeric-string The number of affected rows. * * @throws Exception */ - public function execute() + public function executeStatement(): int|string { - if ($this->type === self::SELECT) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4578', - 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.', - ); - - return $this->executeQuery(); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4578', - 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.', - ); - - return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); + return $this->connection->executeStatement($this->getSQL(), $this->params, $this->types); } /** @@ -405,34 +329,14 @@ public function execute() * * @return string The SQL query string. */ - public function getSQL() + public function getSQL(): string { - if ($this->sql !== null && $this->state === self::STATE_CLEAN) { - return $this->sql; - } - - switch ($this->type) { - case self::INSERT: - $sql = $this->getSQLForInsert(); - break; - - case self::DELETE: - $sql = $this->getSQLForDelete(); - break; - - case self::UPDATE: - $sql = $this->getSQLForUpdate(); - break; - - case self::SELECT: - $sql = $this->getSQLForSelect(); - break; - } - - $this->state = self::STATE_CLEAN; - $this->sql = $sql; - - return $sql; + return $this->sql ??= match ($this->type) { + QueryType::INSERT => $this->getSQLForInsert(), + QueryType::DELETE => $this->getSQLForDelete(), + QueryType::UPDATE => $this->getSQLForUpdate(), + QueryType::SELECT => $this->getSQLForSelect(), + }; } /** @@ -446,26 +350,17 @@ public function getSQL() * ->setParameter('user_id', 1); * * - * @param int|string $key Parameter position or name - * @param mixed $value Parameter value - * @param int|string|Type|null $type Parameter type + * @param int<0, max>|string $key Parameter position or name * * @return $this This QueryBuilder instance. */ - public function setParameter($key, $value, $type = ParameterType::STRING) - { - if ($type !== null) { - $this->paramTypes[$key] = $type; - } else { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5550', - 'Using NULL as prepared statement parameter type is deprecated.' - . 'Omit or use ParameterType::STRING instead', - ); - } - + public function setParameter( + int|string $key, + mixed $value, + string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING, + ): self { $this->params[$key] = $value; + $this->types[$key] = $type; return $this; } @@ -484,15 +379,15 @@ public function setParameter($key, $value, $type = ParameterType::STRING) * )); * * - * @param list|array $params Parameters to set - * @param array|array $types Parameter types + * @param list|array $params + * @psalm-param WrapperParameterTypeArray $types * * @return $this This QueryBuilder instance. */ - public function setParameters(array $params, array $types = []) + public function setParameters(array $params, array $types = []): self { - $this->paramTypes = $types; - $this->params = $params; + $this->params = $params; + $this->types = $types; return $this; } @@ -502,7 +397,7 @@ public function setParameters(array $params, array $types = []) * * @return list|array The currently defined query parameters */ - public function getParameters() + public function getParameters(): array { return $this->params; } @@ -510,11 +405,11 @@ public function getParameters() /** * Gets a (previously set) query parameter of the query being constructed. * - * @param mixed $key The key (index or name) of the bound parameter. + * @param string|int $key The key (index or name) of the bound parameter. * * @return mixed The value of the bound parameter. */ - public function getParameter($key) + public function getParameter(string|int $key): mixed { return $this->params[$key] ?? null; } @@ -522,24 +417,21 @@ public function getParameter($key) /** * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. * - * @return array|array The currently defined - * query parameter types + * @psalm-return WrapperParameterTypeArray */ - public function getParameterTypes() + public function getParameterTypes(): array { - return $this->paramTypes; + return $this->types; } /** * Gets a (previously set) query parameter type of the query being constructed. * * @param int|string $key The key of the bound parameter type - * - * @return int|string|Type The value of the bound parameter type */ - public function getParameterType($key) + public function getParameterType(int|string $key): string|ParameterType|Type|ArrayParameterType { - return $this->paramTypes[$key] ?? ParameterType::STRING; + return $this->types[$key] ?? ParameterType::STRING; } /** @@ -549,11 +441,12 @@ public function getParameterType($key) * * @return $this This QueryBuilder instance. */ - public function setFirstResult($firstResult) + public function setFirstResult(int $firstResult): self { - $this->state = self::STATE_DIRTY; $this->firstResult = $firstResult; + $this->sql = null; + return $this; } @@ -562,7 +455,7 @@ public function setFirstResult($firstResult) * * @return int The position of the first result. */ - public function getFirstResult() + public function getFirstResult(): int { return $this->firstResult; } @@ -574,11 +467,12 @@ public function getFirstResult() * * @return $this This QueryBuilder instance. */ - public function setMaxResults($maxResults) + public function setMaxResults(?int $maxResults): self { - $this->state = self::STATE_DIRTY; $this->maxResults = $maxResults; + $this->sql = null; + return $this; } @@ -588,7 +482,7 @@ public function setMaxResults($maxResults) * * @return int|null The maximum number of results. */ - public function getMaxResults() + public function getMaxResults(): ?int { return $this->maxResults; } @@ -598,61 +492,11 @@ public function getMaxResults() * * @return $this */ - public function forUpdate(int $conflictResolutionMode = ConflictResolutionMode::ORDINARY): self + public function forUpdate(ConflictResolutionMode $conflictResolutionMode = ConflictResolutionMode::ORDINARY): self { - $this->state = self::STATE_DIRTY; + $this->forUpdate = new ForUpdate($conflictResolutionMode); - $this->sqlParts['for_update'] = new ForUpdate($conflictResolutionMode); - - return $this; - } - - /** - * Either appends to or replaces a single, generic query part. - * - * The available parts are: 'select', 'from', 'set', 'where', - * 'groupBy', 'having' and 'orderBy'. - * - * @param string $sqlPartName - * @param mixed $sqlPart - * @param bool $append - * - * @return $this This QueryBuilder instance. - */ - public function add($sqlPartName, $sqlPart, $append = false) - { - $isArray = is_array($sqlPart); - $isMultiple = is_array($this->sqlParts[$sqlPartName]); - - if ($isMultiple && ! $isArray) { - $sqlPart = [$sqlPart]; - } - - $this->state = self::STATE_DIRTY; - - if ($append) { - if ( - $sqlPartName === 'orderBy' - || $sqlPartName === 'groupBy' - || $sqlPartName === 'select' - || $sqlPartName === 'set' - ) { - foreach ($sqlPart as $part) { - $this->sqlParts[$sqlPartName][] = $part; - } - } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { - $key = key($sqlPart); - $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; - } elseif ($isMultiple) { - $this->sqlParts[$sqlPartName][] = $sqlPart; - } else { - $this->sqlParts[$sqlPartName] = $sqlPart; - } - - return $this; - } - - $this->sqlParts[$sqlPartName] = $sqlPart; + $this->sql = null; return $this; } @@ -661,8 +505,6 @@ public function add($sqlPartName, $sqlPart, $append = false) * Specifies an item that is to be returned in the query result. * Replaces any previously specified selections, if any. * - * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. - * * * $qb = $conn->createQueryBuilder() * ->select('u.id', 'p.id') @@ -670,31 +512,19 @@ public function add($sqlPartName, $sqlPart, $append = false) * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * * - * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. - * Pass each value as an individual argument. + * @param string ...$expressions The selection expressions. * * @return $this This QueryBuilder instance. */ - public function select($select = null/*, string ...$selects*/) + public function select(string ...$expressions): self { - $this->type = self::SELECT; + $this->type = QueryType::SELECT; - if ($select === null) { - return $this; - } + $this->select = $expressions; - if (is_array($select)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . - 'pass each value as an individual variadic argument instead.', - ); - } - - $selects = is_array($select) ? $select : func_get_args(); + $this->sql = null; - return $this->add('select', $selects); + return $this; } /** @@ -709,10 +539,10 @@ public function select($select = null/*, string ...$selects*/) * * @return $this This QueryBuilder instance. */ - public function distinct(/* bool $distinct = true */): self + public function distinct(bool $distinct = true): self { - $this->sqlParts['distinct'] = func_num_args() < 1 || func_get_arg(0); - $this->state = self::STATE_DIRTY; + $this->distinct = $distinct; + $this->sql = null; return $this; } @@ -720,8 +550,6 @@ public function distinct(/* bool $distinct = true */): self /** * Adds an item that is to be returned in the query result. * - * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. - * * * $qb = $conn->createQueryBuilder() * ->select('u.id') @@ -730,31 +558,20 @@ public function distinct(/* bool $distinct = true */): self * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); * * - * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. - * Pass each value as an individual argument. + * @param string $expression The selection expression. + * @param string ...$expressions Additional selection expressions. * * @return $this This QueryBuilder instance. */ - public function addSelect($select = null/*, string ...$selects*/) + public function addSelect(string $expression, string ...$expressions): self { - $this->type = self::SELECT; + $this->type = QueryType::SELECT; - if ($select === null) { - return $this; - } - - if (is_array($select)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . - 'pass each value as an individual variadic argument instead.', - ); - } + $this->select = array_merge($this->select, [$expression], $expressions); - $selects = is_array($select) ? $select : func_get_args(); + $this->sql = null; - return $this->add('select', $selects, true); + return $this; } /** @@ -763,28 +580,24 @@ public function addSelect($select = null/*, string ...$selects*/) * * * $qb = $conn->createQueryBuilder() - * ->delete('users', 'u') - * ->where('u.id = :user_id') + * ->delete('users') + * ->where('users.id = :user_id') * ->setParameter(':user_id', 1); * * - * @param string $delete The table whose rows are subject to the deletion. - * @param string $alias The table alias used in the constructed query. + * @param string $table The table whose rows are subject to the deletion. * * @return $this This QueryBuilder instance. */ - public function delete($delete = null, $alias = null) + public function delete(string $table): self { - $this->type = self::DELETE; + $this->type = QueryType::DELETE; - if ($delete === null) { - return $this; - } + $this->table = $table; + + $this->sql = null; - return $this->add('from', [ - 'table' => $delete, - 'alias' => $alias, - ]); + return $this; } /** @@ -793,28 +606,24 @@ public function delete($delete = null, $alias = null) * * * $qb = $conn->createQueryBuilder() - * ->update('counters', 'c') - * ->set('c.value', 'c.value + 1') - * ->where('c.id = ?'); + * ->update('counters') + * ->set('counters.value', 'counters.value + 1') + * ->where('counters.id = ?'); * * - * @param string $update The table whose rows are subject to the update. - * @param string $alias The table alias used in the constructed query. + * @param string $table The table whose rows are subject to the update. * * @return $this This QueryBuilder instance. */ - public function update($update = null, $alias = null) + public function update(string $table): self { - $this->type = self::UPDATE; + $this->type = QueryType::UPDATE; - if ($update === null) { - return $this; - } + $this->table = $table; - return $this->add('from', [ - 'table' => $update, - 'alias' => $alias, - ]); + $this->sql = null; + + return $this; } /** @@ -832,19 +641,19 @@ public function update($update = null, $alias = null) * ); * * - * @param string $insert The table into which the rows should be inserted. + * @param string $table The table into which the rows should be inserted. * * @return $this This QueryBuilder instance. */ - public function insert($insert = null) + public function insert(string $table): self { - $this->type = self::INSERT; + $this->type = QueryType::INSERT; - if ($insert === null) { - return $this; - } + $this->table = $table; + + $this->sql = null; - return $this->add('from', ['table' => $insert]); + return $this; } /** @@ -857,17 +666,18 @@ public function insert($insert = null) * ->from('users', 'u') * * - * @param string $from The table. + * @param string $table The table. * @param string|null $alias The alias of the table. * * @return $this This QueryBuilder instance. */ - public function from($from, $alias = null) + public function from(string $table, ?string $alias = null): self { - return $this->add('from', [ - 'table' => $from, - 'alias' => $alias, - ], true); + $this->from[] = new From($table, $alias); + + $this->sql = null; + + return $this; } /** @@ -887,7 +697,7 @@ public function from($from, $alias = null) * * @return $this This QueryBuilder instance. */ - public function join($fromAlias, $join, $alias, $condition = null) + public function join(string $fromAlias, string $join, string $alias, ?string $condition = null): self { return $this->innerJoin($fromAlias, $join, $alias, $condition); } @@ -909,16 +719,13 @@ public function join($fromAlias, $join, $alias, $condition = null) * * @return $this This QueryBuilder instance. */ - public function innerJoin($fromAlias, $join, $alias, $condition = null) + public function innerJoin(string $fromAlias, string $join, string $alias, ?string $condition = null): self { - return $this->add('join', [ - $fromAlias => [ - 'joinType' => 'inner', - 'joinTable' => $join, - 'joinAlias' => $alias, - 'joinCondition' => $condition, - ], - ], true); + $this->join[$fromAlias][] = Join::inner($join, $alias, $condition); + + $this->sql = null; + + return $this; } /** @@ -938,16 +745,13 @@ public function innerJoin($fromAlias, $join, $alias, $condition = null) * * @return $this This QueryBuilder instance. */ - public function leftJoin($fromAlias, $join, $alias, $condition = null) + public function leftJoin(string $fromAlias, string $join, string $alias, ?string $condition = null): self { - return $this->add('join', [ - $fromAlias => [ - 'joinType' => 'left', - 'joinTable' => $join, - 'joinAlias' => $alias, - 'joinCondition' => $condition, - ], - ], true); + $this->join[$fromAlias][] = Join::left($join, $alias, $condition); + + $this->sql = null; + + return $this; } /** @@ -967,16 +771,13 @@ public function leftJoin($fromAlias, $join, $alias, $condition = null) * * @return $this This QueryBuilder instance. */ - public function rightJoin($fromAlias, $join, $alias, $condition = null) + public function rightJoin(string $fromAlias, string $join, string $alias, ?string $condition = null): self { - return $this->add('join', [ - $fromAlias => [ - 'joinType' => 'right', - 'joinTable' => $join, - 'joinAlias' => $alias, - 'joinCondition' => $condition, - ], - ], true); + $this->join[$fromAlias][] = Join::right($join, $alias, $condition); + + $this->sql = null; + + return $this; } /** @@ -994,9 +795,13 @@ public function rightJoin($fromAlias, $join, $alias, $condition = null) * * @return $this This QueryBuilder instance. */ - public function set($key, $value) + public function set(string $key, string $value): self { - return $this->add('set', $key . ' = ' . $value, true); + $this->set[] = $key . ' = ' . $value; + + $this->sql = null; + + return $this; } /** @@ -1021,17 +826,18 @@ public function set($key, $value) * ->where($or); * * - * @param mixed $predicates The restriction predicates. + * @param string|CompositeExpression $predicate The WHERE clause predicate. + * @param string|CompositeExpression ...$predicates Additional WHERE clause predicates. * * @return $this This QueryBuilder instance. */ - public function where($predicates) + public function where(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) { - $predicates = CompositeExpression::and(...func_get_args()); - } + $this->where = $this->createPredicate($predicate, ...$predicates); + + $this->sql = null; - return $this->add('where', $predicates); + return $this; } /** @@ -1048,23 +854,23 @@ public function where($predicates) * * @see where() * - * @param mixed $where The query restrictions. + * @param string|CompositeExpression $predicate The predicate to append. + * @param string|CompositeExpression ...$predicates Additional predicates to append. * * @return $this This QueryBuilder instance. */ - public function andWhere($where) + public function andWhere(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - $args = func_get_args(); - $where = $this->getQueryPart('where'); - - if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { - $where = $where->with(...$args); - } else { - array_unshift($args, $where); - $where = CompositeExpression::and(...$args); - } + $this->where = $this->appendToPredicate( + $this->where, + CompositeExpression::TYPE_AND, + $predicate, + ...$predicates, + ); - return $this->add('where', $where, true); + $this->sql = null; + + return $this; } /** @@ -1081,31 +887,24 @@ public function andWhere($where) * * @see where() * - * @param mixed $where The WHERE statement. + * @param string|CompositeExpression $predicate The predicate to append. + * @param string|CompositeExpression ...$predicates Additional predicates to append. * * @return $this This QueryBuilder instance. */ - public function orWhere($where) + public function orWhere(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - $args = func_get_args(); - $where = $this->getQueryPart('where'); - - if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { - $where = $where->with(...$args); - } else { - array_unshift($args, $where); - $where = CompositeExpression::or(...$args); - } + $this->where = $this->appendToPredicate($this->where, CompositeExpression::TYPE_OR, $predicate, ...$predicates); + + $this->sql = null; - return $this->add('where', $where, true); + return $this; } /** - * Specifies a grouping over the results of the query. + * Specifies one or more grouping expressions over the results of the query. * Replaces any previously specified groupings, if any. * - * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. - * * * $qb = $conn->createQueryBuilder() * ->select('u.name') @@ -1113,35 +912,22 @@ public function orWhere($where) * ->groupBy('u.id'); * * - * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. - * Pass each value as an individual argument. + * @param string $expression The grouping expression + * @param string ...$expressions Additional grouping expressions * * @return $this This QueryBuilder instance. */ - public function groupBy($groupBy/*, string ...$groupBys*/) + public function groupBy(string $expression, string ...$expressions): self { - if (is_array($groupBy) && count($groupBy) === 0) { - return $this; - } + $this->groupBy = array_merge([$expression], $expressions); - if (is_array($groupBy)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . - 'pass each value as an individual variadic argument instead.', - ); - } + $this->sql = null; - $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); - - return $this->add('groupBy', $groupBy, false); + return $this; } /** - * Adds a grouping expression to the query. - * - * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. + * Adds one or more grouping expressions to the query. * * * $qb = $conn->createQueryBuilder() @@ -1151,29 +937,18 @@ public function groupBy($groupBy/*, string ...$groupBys*/) * ->addGroupBy('u.createdAt'); * * - * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. - * Pass each value as an individual argument. + * @param string $expression The grouping expression + * @param string ...$expressions Additional grouping expressions * * @return $this This QueryBuilder instance. */ - public function addGroupBy($groupBy/*, string ...$groupBys*/) + public function addGroupBy(string $expression, string ...$expressions): self { - if (is_array($groupBy) && count($groupBy) === 0) { - return $this; - } - - if (is_array($groupBy)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3837', - 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . - 'pass each value as an individual variadic argument instead.', - ); - } + $this->groupBy = array_merge($this->groupBy, [$expression], $expressions); - $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); + $this->sql = null; - return $this->add('groupBy', $groupBy, true); + return $this; } /** @@ -1195,9 +970,9 @@ public function addGroupBy($groupBy/*, string ...$groupBys*/) * * @return $this This QueryBuilder instance. */ - public function setValue($column, $value) + public function setValue(string $column, string $value): self { - $this->sqlParts['values'][$column] = $value; + $this->values[$column] = $value; return $this; } @@ -1217,220 +992,161 @@ public function setValue($column, $value) * ); * * - * @param mixed[] $values The values to specify for the insert query indexed by column names. + * @param array $values The values to specify for the insert query indexed by column names. * * @return $this This QueryBuilder instance. */ - public function values(array $values) + public function values(array $values): self { - return $this->add('values', $values); + $this->values = $values; + + $this->sql = null; + + return $this; } /** * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any. * - * @param mixed $having The restriction over the groups. + * @param string|CompositeExpression $predicate The HAVING clause predicate. + * @param string|CompositeExpression ...$predicates Additional HAVING clause predicates. * * @return $this This QueryBuilder instance. */ - public function having($having) + public function having(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) { - $having = CompositeExpression::and(...func_get_args()); - } + $this->having = $this->createPredicate($predicate, ...$predicates); + + $this->sql = null; - return $this->add('having', $having); + return $this; } /** * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions. * - * @param mixed $having The restriction to append. + * @param string|CompositeExpression $predicate The predicate to append. + * @param string|CompositeExpression ...$predicates Additional predicates to append. * * @return $this This QueryBuilder instance. */ - public function andHaving($having) + public function andHaving(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - $args = func_get_args(); - $having = $this->getQueryPart('having'); - - if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { - $having = $having->with(...$args); - } else { - array_unshift($args, $having); - $having = CompositeExpression::and(...$args); - } + $this->having = $this->appendToPredicate( + $this->having, + CompositeExpression::TYPE_AND, + $predicate, + ...$predicates, + ); + + $this->sql = null; - return $this->add('having', $having); + return $this; } /** * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions. * - * @param mixed $having The restriction to add. + * @param string|CompositeExpression $predicate The predicate to append. + * @param string|CompositeExpression ...$predicates Additional predicates to append. * * @return $this This QueryBuilder instance. */ - public function orHaving($having) + public function orHaving(string|CompositeExpression $predicate, string|CompositeExpression ...$predicates): self { - $args = func_get_args(); - $having = $this->getQueryPart('having'); - - if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { - $having = $having->with(...$args); - } else { - array_unshift($args, $having); - $having = CompositeExpression::or(...$args); - } + $this->having = $this->appendToPredicate( + $this->having, + CompositeExpression::TYPE_OR, + $predicate, + ...$predicates, + ); - return $this->add('having', $having); - } + $this->sql = null; - /** - * Specifies an ordering for the query results. - * Replaces any previously specified orderings, if any. - * - * @param string $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function orderBy($sort, $order = null) - { - return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), false); - } - - /** - * Adds an ordering to the query results. - * - * @param string $sort The ordering expression. - * @param string $order The ordering direction. - * - * @return $this This QueryBuilder instance. - */ - public function addOrderBy($sort, $order = null) - { - return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), true); + return $this; } /** - * Gets a query part by its name. - * - * @deprecated The query parts are implementation details and should not be relied upon. - * - * @param string $queryPartName - * - * @return mixed + * Creates a CompositeExpression from one or more predicates combined by the AND logic. */ - public function getQueryPart($queryPartName) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6179', - 'Getting query parts is deprecated as they are implementation details.', - ); + private function createPredicate( + string|CompositeExpression $predicate, + string|CompositeExpression ...$predicates, + ): string|CompositeExpression { + if (count($predicates) === 0) { + return $predicate; + } - return $this->sqlParts[$queryPartName]; + return new CompositeExpression(CompositeExpression::TYPE_AND, $predicate, ...$predicates); } /** - * Gets all query parts. - * - * @deprecated The query parts are implementation details and should not be relied upon. - * - * @return mixed[] + * Appends the given predicates combined by the given type of logic to the current predicate. */ - public function getQueryParts() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6179', - 'Getting query parts is deprecated as they are implementation details.', - ); + private function appendToPredicate( + string|CompositeExpression|null $currentPredicate, + string $type, + string|CompositeExpression ...$predicates, + ): string|CompositeExpression { + if ($currentPredicate instanceof CompositeExpression && $currentPredicate->getType() === $type) { + return $currentPredicate->with(...$predicates); + } - return $this->sqlParts; + if ($currentPredicate !== null) { + array_unshift($predicates, $currentPredicate); + } elseif (count($predicates) === 1) { + return $predicates[0]; + } + + return new CompositeExpression($type, ...$predicates); } /** - * Resets SQL parts. - * - * @deprecated Use the dedicated reset*() methods instead. + * Specifies an ordering for the query results. + * Replaces any previously specified orderings, if any. * - * @param string[]|null $queryPartNames + * @param string $sort The ordering expression. + * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. */ - public function resetQueryParts($queryPartNames = null) + public function orderBy(string $sort, ?string $order = null): self { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6193', - '%s() is deprecated, instead use dedicated reset methods for the parts that shall be reset.', - __METHOD__, - ); - - $queryPartNames ??= array_keys($this->sqlParts); + $orderBy = $sort; - foreach ($queryPartNames as $queryPartName) { - $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; + if ($order !== null) { + $orderBy .= ' ' . $order; } - $this->state = self::STATE_DIRTY; + $this->orderBy = [$orderBy]; + + $this->sql = null; return $this; } /** - * Resets a single SQL part. - * - * @deprecated Use the dedicated reset*() methods instead. + * Adds an ordering to the query results. * - * @param string $queryPartName + * @param string $sort The ordering expression. + * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. */ - public function resetQueryPart($queryPartName) + public function addOrderBy(string $sort, ?string $order = null): self { - if ($queryPartName === 'distinct') { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6193', - 'Calling %s() with "distinct" is deprecated, call distinct(false) instead.', - __METHOD__, - ); + $orderBy = $sort; - return $this->distinct(false); + if ($order !== null) { + $orderBy .= ' ' . $order; } - $newMethodName = 'reset' . ucfirst($queryPartName); - if (array_key_exists($queryPartName, self::SQL_PARTS_DEFAULTS) && method_exists($this, $newMethodName)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6193', - 'Calling %s() with "%s" is deprecated, call %s() instead.', - __METHOD__, - $queryPartName, - $newMethodName, - ); + $this->orderBy[] = $orderBy; - return $this->$newMethodName(); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6193', - 'Calling %s() with "%s" is deprecated without replacement.', - __METHOD__, - $queryPartName, - $newMethodName, - ); - - $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; - - $this->state = self::STATE_DIRTY; + $this->sql = null; return $this; } @@ -1442,9 +1158,8 @@ public function resetQueryPart($queryPartName) */ public function resetWhere(): self { - $this->sqlParts['where'] = self::SQL_PARTS_DEFAULTS['where']; - - $this->state = self::STATE_DIRTY; + $this->where = null; + $this->sql = null; return $this; } @@ -1456,9 +1171,8 @@ public function resetWhere(): self */ public function resetGroupBy(): self { - $this->sqlParts['groupBy'] = self::SQL_PARTS_DEFAULTS['groupBy']; - - $this->state = self::STATE_DIRTY; + $this->groupBy = []; + $this->sql = null; return $this; } @@ -1470,9 +1184,8 @@ public function resetGroupBy(): self */ public function resetHaving(): self { - $this->sqlParts['having'] = self::SQL_PARTS_DEFAULTS['having']; - - $this->state = self::STATE_DIRTY; + $this->having = null; + $this->sql = null; return $this; } @@ -1484,9 +1197,8 @@ public function resetHaving(): self */ public function resetOrderBy(): self { - $this->sqlParts['orderBy'] = self::SQL_PARTS_DEFAULTS['orderBy']; - - $this->state = self::STATE_DIRTY; + $this->orderBy = []; + $this->sql = null; return $this; } @@ -1494,25 +1206,29 @@ public function resetOrderBy(): self /** @throws Exception */ private function getSQLForSelect(): string { + if (count($this->select) === 0) { + throw new QueryException('No SELECT expressions given. Please use select() or addSelect().'); + } + return $this->connection->getDatabasePlatform() ->createSelectSQLBuilder() ->buildSQL( new SelectQuery( - $this->sqlParts['distinct'], - $this->sqlParts['select'], + $this->distinct, + $this->select, $this->getFromClauses(), - $this->sqlParts['where'], - $this->sqlParts['groupBy'], - $this->sqlParts['having'], - $this->sqlParts['orderBy'], + $this->where !== null ? (string) $this->where : null, + $this->groupBy, + $this->having !== null ? (string) $this->having : null, + $this->orderBy, new Limit($this->maxResults, $this->firstResult), - $this->sqlParts['for_update'], + $this->forUpdate, ), ); } /** - * @return string[] + * @return array * * @throws QueryException */ @@ -1521,14 +1237,13 @@ private function getFromClauses(): array $fromClauses = []; $knownAliases = []; - // Loop through all FROM clauses - foreach ($this->sqlParts['from'] as $from) { - if ($from['alias'] === null) { - $tableSql = $from['table']; - $tableReference = $from['table']; + foreach ($this->from as $from) { + if ($from->alias === null || $from->alias === $from->table) { + $tableSql = $from->table; + $tableReference = $from->table; } else { - $tableSql = $from['table'] . ' ' . $from['alias']; - $tableReference = $from['alias']; + $tableSql = $from->table . ' ' . $from->alias; + $tableReference = $from->alias; } $knownAliases[$tableReference] = true; @@ -1542,15 +1257,15 @@ private function getFromClauses(): array } /** - * @param array $knownAliases + * @param array $knownAliases * * @throws QueryException */ private function verifyAllAliasesAreKnown(array $knownAliases): void { - foreach ($this->sqlParts['join'] as $fromAlias => $joins) { + foreach ($this->join as $fromAlias => $joins) { if (! isset($knownAliases[$fromAlias])) { - throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); + throw UnknownAlias::new($fromAlias, array_keys($knownAliases)); } } } @@ -1560,9 +1275,9 @@ private function verifyAllAliasesAreKnown(array $knownAliases): void */ private function getSQLForInsert(): string { - return 'INSERT INTO ' . $this->sqlParts['from']['table'] . - ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . - ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; + return 'INSERT INTO ' . $this->table . + ' (' . implode(', ', array_keys($this->values)) . ')' . + ' VALUES(' . implode(', ', $this->values) . ')'; } /** @@ -1570,12 +1285,14 @@ private function getSQLForInsert(): string */ private function getSQLForUpdate(): string { - $table = $this->sqlParts['from']['table'] - . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'UPDATE ' . $this->table + . ' SET ' . implode(', ', $this->set); - return 'UPDATE ' . $table - . ' SET ' . implode(', ', $this->sqlParts['set']) - . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + if ($this->where !== null) { + $query .= ' WHERE ' . $this->where; + } + + return $query; } /** @@ -1583,11 +1300,13 @@ private function getSQLForUpdate(): string */ private function getSQLForDelete(): string { - $table = $this->sqlParts['from']['table'] - . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); + $query = 'DELETE FROM ' . $this->table; + + if ($this->where !== null) { + $query .= ' WHERE ' . $this->where; + } - return 'DELETE FROM ' . $table - . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); + return $query; } /** @@ -1596,7 +1315,7 @@ private function getSQLForDelete(): string * * @return string The string representation of this QueryBuilder. */ - public function __toString() + public function __toString(): string { return $this->getSQL(); } @@ -1621,14 +1340,15 @@ public function __toString() * * @link http://www.zetacomponents.org * - * @param mixed $value - * @param int|string|Type|null $type - * @param string $placeHolder The name to bind with. The string must start with a colon ':'. + * @param string|null $placeHolder The name to bind with. The string must start with a colon ':'. * * @return string the placeholder name used. */ - public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null) - { + public function createNamedParameter( + mixed $value, + string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING, + ?string $placeHolder = null, + ): string { if ($placeHolder === null) { $this->boundCounter++; $placeHolder = ':dcValue' . $this->boundCounter; @@ -1655,14 +1375,11 @@ public function createNamedParameter($value, $type = ParameterType::STRING, $pla * ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING)) * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING)) * - * - * @param mixed $value - * @param int|string|Type|null $type - * - * @return string */ - public function createPositionalParameter($value, $type = ParameterType::STRING) - { + public function createPositionalParameter( + mixed $value, + string|ParameterType|Type|ArrayParameterType $type = ParameterType::STRING, + ): string { $this->setParameter($this->boundCounter, $value, $type); $this->boundCounter++; @@ -1670,33 +1387,34 @@ public function createPositionalParameter($value, $type = ParameterType::STRING) } /** - * @param string $fromAlias - * @param array $knownAliases + * @param array $knownAliases * * @throws QueryException */ - private function getSQLForJoins($fromAlias, array &$knownAliases): string + private function getSQLForJoins(string $fromAlias, array &$knownAliases): string { $sql = ''; - if (isset($this->sqlParts['join'][$fromAlias])) { - foreach ($this->sqlParts['join'][$fromAlias] as $join) { - if (array_key_exists($join['joinAlias'], $knownAliases)) { - throw QueryException::nonUniqueAlias((string) $join['joinAlias'], array_keys($knownAliases)); - } - - $sql .= ' ' . strtoupper($join['joinType']) - . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']; - if ($join['joinCondition'] !== null) { - $sql .= ' ON ' . $join['joinCondition']; - } + if (! isset($this->join[$fromAlias])) { + return $sql; + } - $knownAliases[$join['joinAlias']] = true; + foreach ($this->join[$fromAlias] as $join) { + if (array_key_exists($join->alias, $knownAliases)) { + throw NonUniqueAlias::new($join->alias, array_keys($knownAliases)); } - foreach ($this->sqlParts['join'][$fromAlias] as $join) { - $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); + $sql .= ' ' . $join->type . ' JOIN ' . $join->table . ' ' . $join->alias; + + if ($join->condition !== null) { + $sql .= ' ON ' . $join->condition; } + + $knownAliases[$join->alias] = true; + } + + foreach ($this->join[$fromAlias] as $join) { + $sql .= $this->getSQLForJoins($join->alias, $knownAliases); } return $sql; @@ -1704,25 +1422,27 @@ private function getSQLForJoins($fromAlias, array &$knownAliases): string /** * Deep clone of all expression objects in the SQL parts. - * - * @return void */ public function __clone() { - foreach ($this->sqlParts as $part => $elements) { - if (is_array($this->sqlParts[$part])) { - foreach ($this->sqlParts[$part] as $idx => $element) { - if (! is_object($element)) { - continue; - } - - $this->sqlParts[$part][$idx] = clone $element; - } - } elseif (is_object($elements)) { - $this->sqlParts[$part] = clone $elements; + foreach ($this->from as $key => $from) { + $this->from[$key] = clone $from; + } + + foreach ($this->join as $fromAlias => $joins) { + foreach ($joins as $key => $join) { + $this->join[$fromAlias][$key] = clone $join; } } + if (is_object($this->where)) { + $this->where = clone $this->where; + } + + if (is_object($this->having)) { + $this->having = clone $this->having; + } + foreach ($this->params as $name => $param) { if (! is_object($param)) { continue; @@ -1734,7 +1454,7 @@ public function __clone() /** * Enables caching of the results of this query, for given amount of seconds - * and optionally specified witch key to use for the cache entry. + * and optionally specified which key to use for the cache entry. * * @return $this */ diff --git a/doctrine/dbal/src/Query/QueryException.php b/doctrine/dbal/src/Query/QueryException.php index 90d1f47d9..eac383640 100644 --- a/doctrine/dbal/src/Query/QueryException.php +++ b/doctrine/dbal/src/Query/QueryException.php @@ -1,37 +1,12 @@ distinct = $distinct; - $this->columns = $columns; - $this->from = $from; - $this->where = $where; - $this->groupBy = $groupBy; - $this->having = $having; - $this->orderBy = $orderBy; - $this->limit = $limit; - $this->forUpdate = $forUpdate; } public function isDistinct(): bool diff --git a/doctrine/dbal/src/Result.php b/doctrine/dbal/src/Result.php index fdc7daa94..6bc7c6dc0 100644 --- a/doctrine/dbal/src/Result.php +++ b/doctrine/dbal/src/Result.php @@ -7,23 +7,17 @@ use Doctrine\DBAL\Driver\Exception as DriverException; use Doctrine\DBAL\Driver\Result as DriverResult; use Doctrine\DBAL\Exception\NoKeyValue; -use Doctrine\Deprecations\Deprecation; -use LogicException; use Traversable; use function array_shift; -use function func_num_args; +use function assert; +use function count; class Result { - private DriverResult $result; - private Connection $connection; - /** @internal The result can be only instantiated by {@see Connection} or {@see Statement}. */ - public function __construct(DriverResult $result, Connection $connection) + public function __construct(private readonly DriverResult $result, private readonly Connection $connection) { - $this->result = $result; - $this->connection = $connection; } /** @@ -33,7 +27,7 @@ public function __construct(DriverResult $result, Connection $connection) * * @throws Exception */ - public function fetchNumeric() + public function fetchNumeric(): array|false { try { return $this->result->fetchNumeric(); @@ -49,7 +43,7 @@ public function fetchNumeric() * * @throws Exception */ - public function fetchAssociative() + public function fetchAssociative(): array|false { try { return $this->result->fetchAssociative(); @@ -61,11 +55,9 @@ public function fetchAssociative() /** * Returns the first value of the next row of the result or FALSE if there are no more rows. * - * @return mixed|false - * * @throws Exception */ - public function fetchOne() + public function fetchOne(): mixed { try { return $this->result->fetchOne(); @@ -119,8 +111,10 @@ public function fetchAllKeyValue(): array $data = []; - foreach ($this->fetchAllNumeric() as [$key, $value]) { - $data[$key] = $value; + foreach ($this->fetchAllNumeric() as $row) { + assert(count($row) >= 2); + [$key, $value] = $row; + $data[$key] = $value; } return $data; @@ -184,7 +178,7 @@ public function iterateAssociative(): Traversable } /** - * {@inheritDoc} + * @return Traversable * * @throws Exception */ @@ -192,7 +186,10 @@ public function iterateKeyValue(): Traversable { $this->ensureHasKeyValue(); - foreach ($this->iterateNumeric() as [$key, $value]) { + foreach ($this->iterateNumeric() as $row) { + assert(count($row) >= 2); + [$key, $value] = $row; + yield $key => $value; } } @@ -224,8 +221,20 @@ public function iterateColumn(): Traversable } } - /** @throws Exception */ - public function rowCount(): int + /** + * Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result. + * + * If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.), + * some database drivers may return the number of rows returned by that query. However, this behaviour + * is not guaranteed for all drivers and should not be relied on in portable applications. + * + * If the number of rows exceeds {@see PHP_INT_MAX}, it might be returned as string if the driver supports it. + * + * @return int|numeric-string + * + * @throws Exception + */ + public function rowCount(): int|string { try { return $this->result->rowCount(); @@ -258,82 +267,4 @@ private function ensureHasKeyValue(): void throw NoKeyValue::fromColumnCount($columnCount); } } - - /** - * BC layer for a wide-spread use-case of old DBAL APIs - * - * @deprecated Use {@see fetchNumeric()}, {@see fetchAssociative()} or {@see fetchOne()} instead. - * - * @psalm-param FetchMode::* $mode - * - * @return mixed - * - * @throws Exception - */ - public function fetch(int $mode = FetchMode::ASSOCIATIVE) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4007', - '%s is deprecated, please use fetchNumeric(), fetchAssociative() or fetchOne() instead.', - __METHOD__, - ); - - if (func_num_args() > 1) { - throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); - } - - if ($mode === FetchMode::ASSOCIATIVE) { - return $this->fetchAssociative(); - } - - if ($mode === FetchMode::NUMERIC) { - return $this->fetchNumeric(); - } - - if ($mode === FetchMode::COLUMN) { - return $this->fetchOne(); - } - - throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); - } - - /** - * BC layer for a wide-spread use-case of old DBAL APIs - * - * @deprecated Use {@see fetchAllNumeric()}, {@see fetchAllAssociative()} or {@see fetchFirstColumn()} instead. - * - * @psalm-param FetchMode::* $mode - * - * @return list - * - * @throws Exception - */ - public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4007', - '%s is deprecated, please use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.', - __METHOD__, - ); - - if (func_num_args() > 1) { - throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); - } - - if ($mode === FetchMode::ASSOCIATIVE) { - return $this->fetchAllAssociative(); - } - - if ($mode === FetchMode::NUMERIC) { - return $this->fetchAllNumeric(); - } - - if ($mode === FetchMode::COLUMN) { - return $this->fetchFirstColumn(); - } - - throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); - } } diff --git a/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php index 2e392e661..5f6a77a7a 100644 --- a/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php +++ b/doctrine/dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php @@ -1,8 +1,9 @@ platform = $platform; } - /** - * @return list - * - * @throws Exception - */ + /** @return list */ public function buildSQL(Schema $schema): array { return array_merge( @@ -37,8 +31,6 @@ public function buildSQL(Schema $schema): array * @param list $namespaces * * @return list - * - * @throws Exception */ private function buildNamespaceStatements(array $namespaces): array { @@ -57,8 +49,6 @@ private function buildNamespaceStatements(array $namespaces): array * @param list
$tables * * @return list - * - * @throws Exception */ private function buildTableStatements(array $tables): array { @@ -69,8 +59,6 @@ private function buildTableStatements(array $tables): array * @param list $sequences * * @return list - * - * @throws Exception */ private function buildSequenceStatements(array $sequences): array { diff --git a/doctrine/dbal/src/SQL/Builder/DefaultSelectSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/DefaultSelectSQLBuilder.php index 9dcb7a197..a30120e5e 100644 --- a/doctrine/dbal/src/SQL/Builder/DefaultSelectSQLBuilder.php +++ b/doctrine/dbal/src/SQL/Builder/DefaultSelectSQLBuilder.php @@ -1,9 +1,12 @@ platform = $platform; - $this->forUpdateSQL = $forUpdateSQL; - $this->skipLockedSQL = $skipLockedSQL; + public function __construct( + private readonly AbstractPlatform $platform, + private readonly ?string $forUpdateSQL, + private readonly ?string $skipLockedSQL, + ) { } /** @throws Exception */ @@ -76,14 +75,14 @@ public function buildSQL(SelectQuery $query): string if ($forUpdate !== null) { if ($this->forUpdateSQL === null) { - throw Exception::notSupported('FOR UPDATE'); + throw NotSupported::new('FOR UPDATE'); } $sql .= ' ' . $this->forUpdateSQL; if ($forUpdate->getConflictResolutionMode() === ConflictResolutionMode::SKIP_LOCKED) { if ($this->skipLockedSQL === null) { - throw Exception::notSupported('SKIP LOCKED'); + throw NotSupported::new('SKIP LOCKED'); } $sql .= ' ' . $this->skipLockedSQL; diff --git a/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php index 8de742a31..c0384897f 100644 --- a/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php +++ b/doctrine/dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.php @@ -1,8 +1,9 @@ platform = $platform; } - /** - * @return list - * - * @throws Exception - */ + /** @return list */ public function buildSQL(Schema $schema): array { return array_merge( @@ -46,15 +40,13 @@ private function buildTableStatements(array $tables): array * @param list $sequences * * @return list - * - * @throws Exception */ private function buildSequenceStatements(array $sequences): array { $statements = []; foreach ($sequences as $sequence) { - $statements[] = $this->platform->getDropSequenceSQL($sequence); + $statements[] = $this->platform->getDropSequenceSQL($sequence->getQuotedName($this->platform)); } return $statements; diff --git a/doctrine/dbal/src/SQL/Builder/SelectSQLBuilder.php b/doctrine/dbal/src/SQL/Builder/SelectSQLBuilder.php index ddbf73c03..c013f96a8 100644 --- a/doctrine/dbal/src/SQL/Builder/SelectSQLBuilder.php +++ b/doctrine/dbal/src/SQL/Builder/SelectSQLBuilder.php @@ -1,5 +1,7 @@ isIdentifierQuoted($name)) { $this->_quoted = true; $name = $this->trimQuotes($name); } - if (strpos($name, '.') !== false) { + if (str_contains($name, '.')) { $parts = explode('.', $name); $this->_namespace = $parts[0]; $name = $parts[1]; @@ -62,12 +55,8 @@ protected function _setName($name) /** * Is this asset in the default namespace? - * - * @param string $defaultNamespaceName - * - * @return bool */ - public function isInDefaultNamespace($defaultNamespaceName) + public function isInDefaultNamespace(string $defaultNamespaceName): bool { return $this->_namespace === $defaultNamespaceName || $this->_namespace === null; } @@ -76,10 +65,8 @@ public function isInDefaultNamespace($defaultNamespaceName) * Gets the namespace name of this asset. * * If NULL is returned this means the default namespace is used. - * - * @return string|null */ - public function getNamespaceName() + public function getNamespaceName(): ?string { return $this->_namespace; } @@ -87,12 +74,8 @@ public function getNamespaceName() /** * The shortest name is stripped of the default namespace. All other * namespaced elements are returned as full-qualified names. - * - * @param string|null $defaultNamespaceName - * - * @return string */ - public function getShortestName($defaultNamespaceName) + public function getShortestName(?string $defaultNamespaceName): string { $shortestName = $this->getName(); if ($this->_namespace === $defaultNamespaceName) { @@ -102,78 +85,34 @@ public function getShortestName($defaultNamespaceName) return strtolower($shortestName); } - /** - * The normalized name is full-qualified and lower-cased. Lower-casing is - * actually wrong, but we have to do it to keep our sanity. If you are - * using database objects that only differentiate in the casing (FOO vs - * Foo) then you will NOT be able to use Doctrine Schema abstraction. - * - * Every non-namespaced element is prefixed with the default namespace - * name which is passed as argument to this method. - * - * @deprecated Use {@see getNamespaceName()} and {@see getName()} instead. - * - * @param string $defaultNamespaceName - * - * @return string - */ - public function getFullQualifiedName($defaultNamespaceName) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4814', - 'AbstractAsset::getFullQualifiedName() is deprecated.' - . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.', - ); - - $name = $this->getName(); - if ($this->_namespace === null) { - $name = $defaultNamespaceName . '.' . $name; - } - - return strtolower($name); - } - /** * Checks if this asset's name is quoted. - * - * @return bool */ - public function isQuoted() + public function isQuoted(): bool { return $this->_quoted; } /** * Checks if this identifier is quoted. - * - * @param string $identifier - * - * @return bool */ - protected function isIdentifierQuoted($identifier) + protected function isIdentifierQuoted(string $identifier): bool { return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '['); } /** * Trim quotes from the identifier. - * - * @param string $identifier - * - * @return string */ - protected function trimQuotes($identifier) + protected function trimQuotes(string $identifier): string { return str_replace(['`', '"', '[', ']'], '', $identifier); } /** * Returns the name of this schema asset. - * - * @return string */ - public function getName() + public function getName(): string { if ($this->_namespace !== null) { return $this->_namespace . '.' . $this->_name; @@ -185,10 +124,8 @@ public function getName() /** * Gets the quoted representation of this asset but only if it was defined with one. Otherwise * return the plain unquoted value as inserted. - * - * @return string */ - public function getQuotedName(AbstractPlatform $platform) + public function getQuotedName(AbstractPlatform $platform): string { $keywords = $platform->getReservedKeywordsList(); $parts = explode('.', $this->getName()); @@ -206,13 +143,9 @@ public function getQuotedName(AbstractPlatform $platform) * however building idents automatically for foreign keys, composite keys or such can easily create * very long names. * - * @param string[] $columnNames - * @param string $prefix - * @param int $maxSize - * - * @return string + * @param array $columnNames */ - protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30) + protected function _generateIdentifierName(array $columnNames, string $prefix = '', int $maxSize = 30): string { $hash = implode('', array_map(static function ($column): string { return dechex(crc32($column)); diff --git a/doctrine/dbal/src/Schema/AbstractSchemaManager.php b/doctrine/dbal/src/Schema/AbstractSchemaManager.php index 7e1fd93ff..88ffdadc9 100644 --- a/doctrine/dbal/src/Schema/AbstractSchemaManager.php +++ b/doctrine/dbal/src/Schema/AbstractSchemaManager.php @@ -1,30 +1,22 @@ _conn = $connection; - $this->_platform = $platform; - } - - /** - * Returns the associated platform. - * - * @deprecated Use {@link Connection::getDatabasePlatform()} instead. - * - * @return T - */ - public function getDatabasePlatform() + public function __construct(protected Connection $connection, protected AbstractPlatform $platform) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5387', - 'AbstractSchemaManager::getDatabasePlatform() is deprecated.' - . ' Use Connection::getDatabasePlatform() instead.', - ); - - return $this->_platform; - } - - /** - * Tries any method on the schema manager. Normally a method throws an - * exception when your DBMS doesn't support it or if an error occurs. - * This method allows you to try and method on your SchemaManager - * instance and will return false if it does not work or is not supported. - * - * - * $result = $sm->tryMethod('dropView', 'view_name'); - * - * - * @deprecated - * - * @return mixed - */ - public function tryMethod() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::tryMethod() is deprecated.', - ); - - $args = func_get_args(); - $method = $args[0]; - unset($args[0]); - $args = array_values($args); - - $callback = [$this, $method]; - assert(is_callable($callback)); - - try { - return call_user_func_array($callback, $args); - } catch (Throwable $e) { - return false; - } } /** * Lists the available databases for this connection. * - * @return string[] - * - * @throws Exception - */ - public function listDatabases() - { - $sql = $this->_platform->getListDatabasesSQL(); - - $databases = $this->_conn->fetchAllAssociative($sql); - - return $this->_getPortableDatabasesList($databases); - } - - /** - * Returns a list of all namespaces in the current database. - * - * @deprecated Use {@see listSchemaNames()} instead. - * - * @return string[] + * @return array * * @throws Exception */ - public function listNamespaceNames() + public function listDatabases(): array { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'AbstractSchemaManager::listNamespaceNames() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.', - ); - - $sql = $this->_platform->getListNamespacesSQL(); - - $namespaces = $this->_conn->fetchAllAssociative($sql); - - return $this->getPortableNamespacesList($namespaces); + return array_map(function (array $row): string { + return $this->_getPortableDatabaseDefinition($row); + }, $this->connection->fetchAllAssociative( + $this->platform->getListDatabasesSQL(), + )); } /** @@ -162,35 +57,27 @@ public function listNamespaceNames() */ public function listSchemaNames(): array { - throw Exception::notSupported(__METHOD__); + throw NotSupported::new(__METHOD__); } /** * Lists the available sequences for this connection. * - * @param string|null $database - * - * @return Sequence[] + * @return array * * @throws Exception */ - public function listSequences($database = null) + public function listSequences(): array { - if ($database === null) { - $database = $this->getDatabase(__METHOD__); - } else { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5284', - 'Passing $database to AbstractSchemaManager::listSequences() is deprecated.', - ); - } - - $sql = $this->_platform->getListSequencesSQL($database); - - $sequences = $this->_conn->fetchAllAssociative($sql); - - return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); + return $this->filterAssetNames( + array_map(function (array $row): Sequence { + return $this->_getPortableSequenceDefinition($row); + }, $this->connection->fetchAllAssociative( + $this->platform->getListSequencesSQL( + $this->getDatabase(__METHOD__), + ), + )), + ); } /** @@ -203,51 +90,13 @@ public function listSequences($database = null) * of a table. Where a RDBMS specifies more details, these are held * in the platformDetails array. * - * @param string $table The name of the table. - * @param string|null $database - * - * @return Column[] - * - * @throws Exception - */ - public function listTableColumns($table, $database = null) - { - if ($database === null) { - $database = $this->getDatabase(__METHOD__); - } else { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5284', - 'Passing $database to AbstractSchemaManager::listTableColumns() is deprecated.', - ); - } - - $sql = $this->_platform->getListTableColumnsSQL($table, $database); - - $tableColumns = $this->_conn->fetchAllAssociative($sql); - - return $this->_getPortableTableColumnList($table, $database, $tableColumns); - } - - /** - * @param string $table - * @param string|null $database - * - * @return Column[] + * @return array * * @throws Exception */ - protected function doListTableColumns($table, $database = null): array + public function listTableColumns(string $table): array { - if ($database === null) { - $database = $this->getDatabase(__METHOD__); - } else { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5284', - 'Passing $database to AbstractSchemaManager::doListTableColumns() is deprecated.', - ); - } + $database = $this->getDatabase(__METHOD__); return $this->_getPortableTableColumnList( $table, @@ -262,29 +111,11 @@ protected function doListTableColumns($table, $database = null): array * * Keys of the portable indexes list are all lower-cased. * - * @param string $table The name of the table. - * - * @return Index[] - * - * @throws Exception - */ - public function listTableIndexes($table) - { - $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); - - $tableIndexes = $this->_conn->fetchAllAssociative($sql); - - return $this->_getPortableTableIndexesList($tableIndexes, $table); - } - - /** - * @param string $table - * - * @return Index[] + * @return array * * @throws Exception */ - protected function doListTableIndexes($table): array + public function listTableIndexes(string $table): array { $database = $this->getDatabase(__METHOD__); $table = $this->normalizeName($table); @@ -301,61 +132,37 @@ protected function doListTableIndexes($table): array /** * Returns true if all the given tables exist. * - * The usage of a string $tableNames is deprecated. Pass a one-element array instead. - * - * @param string|string[] $names - * - * @return bool + * @param array $names * * @throws Exception */ - public function tablesExist($names) + public function tablesExist(array $names): bool { - if (is_string($names)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/3580', - 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . - 'Pass a one-element array instead.', - ); - } - - $names = array_map('strtolower', (array) $names); + $names = array_map('strtolower', $names); return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames()))); } - /** - * Returns a list of all tables in the current database. - * - * @return string[] - * - * @throws Exception - */ - public function listTableNames() + public function tableExists(string $tableName): bool { - $sql = $this->_platform->getListTablesSQL(); - - $tables = $this->_conn->fetchAllAssociative($sql); - $tableNames = $this->_getPortableTablesList($tables); - - return $this->filterAssetNames($tableNames); + return $this->tablesExist([$tableName]); } /** - * @return list + * Returns a list of all tables in the current database. + * + * @return array * * @throws Exception */ - protected function doListTableNames(): array + public function listTableNames(): array { - $database = $this->getDatabase(__METHOD__); - return $this->filterAssetNames( - $this->_getPortableTablesList( - $this->selectTableNames($database) - ->fetchAllAssociative(), - ), + array_map(function (array $row): string { + return $this->_getPortableTableDefinition($row); + }, $this->selectTableNames( + $this->getDatabase(__METHOD__), + )->fetchAllAssociative()), ); } @@ -363,16 +170,13 @@ protected function doListTableNames(): array * Filters asset names if they are configured to return only a subset of all * the found elements. * - * @param mixed[] $assetNames + * @param array $assetNames * - * @return mixed[] + * @return array */ - protected function filterAssetNames($assetNames) + private function filterAssetNames(array $assetNames): array { - $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); - if ($filter === null) { - return $assetNames; - } + $filter = $this->connection->getConfiguration()->getSchemaAssetsFilter(); return array_values(array_filter($assetNames, $filter)); } @@ -384,24 +188,7 @@ protected function filterAssetNames($assetNames) * * @throws Exception */ - public function listTables() - { - $tableNames = $this->listTableNames(); - - $tables = []; - foreach ($tableNames as $tableName) { - $tables[] = $this->introspectTable($tableName); - } - - return $tables; - } - - /** - * @return list
- * - * @throws Exception - */ - protected function doListTables(): array + public function listTables(): array { $database = $this->getDatabase(__METHOD__); @@ -410,11 +197,11 @@ protected function doListTables(): array $foreignKeyColumnsByTable = $this->fetchForeignKeyColumnsByTable($database); $tableOptionsByTable = $this->fetchTableOptionsByTable($database); - $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); + $filter = $this->connection->getConfiguration()->getSchemaAssetsFilter(); $tables = []; foreach ($tableColumnsByTable as $tableName => $tableColumns) { - if ($filter !== null && ! $filter($tableName)) { + if (! $filter($tableName)) { continue; } @@ -431,65 +218,6 @@ protected function doListTables(): array return $tables; } - /** - * @deprecated Use {@see introspectTable()} instead. - * - * @param string $name - * - * @return Table - * - * @throws Exception - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - $columns = $this->listTableColumns($name); - $foreignKeys = []; - - if ($this->_platform->supportsForeignKeyConstraints()) { - $foreignKeys = $this->listTableForeignKeys($name); - } - - $indexes = $this->listTableIndexes($name); - - return new Table($name, $columns, $indexes, [], $foreignKeys); - } - - /** - * @param string $name - * - * @throws Exception - */ - protected function doListTableDetails($name): Table - { - $database = $this->getDatabase(__METHOD__); - - $normalizedName = $this->normalizeName($name); - - $tableOptionsByTable = $this->fetchTableOptionsByTable($database, $normalizedName); - - if ($this->_platform->supportsForeignKeyConstraints()) { - $foreignKeys = $this->listTableForeignKeys($name); - } else { - $foreignKeys = []; - } - - return new Table( - $name, - $this->listTableColumns($name, $database), - $this->listTableIndexes($name), - [], - $foreignKeys, - $tableOptionsByTable[$normalizedName] ?? [], - ); - } - /** * An extension point for those platforms where case sensitivity of the object name depends on whether it's quoted. * @@ -506,26 +234,16 @@ protected function normalizeName(string $name): string * Selects names of tables in the specified database. * * @throws Exception - * - * @abstract */ - protected function selectTableNames(string $databaseName): Result - { - throw Exception::notSupported(__METHOD__); - } + abstract protected function selectTableNames(string $databaseName): Result; /** * Selects definitions of table columns in the specified database. If the table name is specified, narrows down * the selection to this table. * * @throws Exception - * - * @abstract */ - protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result - { - throw Exception::notSupported(__METHOD__); - } + abstract protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result; /** * Selects definitions of index columns in the specified database. If the table name is specified, narrows down @@ -533,10 +251,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = * * @throws Exception */ - protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result - { - throw Exception::notSupported(__METHOD__); - } + abstract protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result; /** * Selects definitions of foreign key columns in the specified database. If the table name is specified, @@ -544,10 +259,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = * * @throws Exception */ - protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result - { - throw Exception::notSupported(__METHOD__); - } + abstract protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result; /** * Fetches definitions of table columns in the specified database and returns them grouped by table name. @@ -582,10 +294,6 @@ protected function fetchIndexColumnsByTable(string $databaseName): array */ protected function fetchForeignKeyColumnsByTable(string $databaseName): array { - if (! $this->_platform->supportsForeignKeyConstraints()) { - return []; - } - return $this->fetchAllAssociativeGrouped( $this->selectForeignKeyColumns($databaseName), ); @@ -599,10 +307,7 @@ protected function fetchForeignKeyColumnsByTable(string $databaseName): array * * @throws Exception */ - protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array - { - throw Exception::notSupported(__METHOD__); - } + abstract protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array; /** * Introspects the table with the given name. @@ -611,85 +316,72 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table */ public function introspectTable(string $name): Table { - $table = $this->listTableDetails($name); + $columns = $this->listTableColumns($name); - if ($table->getColumns() === []) { - throw SchemaException::tableDoesNotExist($name); + if ($columns === []) { + throw TableDoesNotExist::new($name); } - return $table; + return new Table( + $name, + $columns, + $this->listTableIndexes($name), + [], + $this->listTableForeignKeys($name), + $this->getTableOptions($name), + ); } /** * Lists the views this connection has. * - * @return View[] + * @return list * * @throws Exception */ - public function listViews() + public function listViews(): array { - $database = $this->_conn->getDatabase(); - $sql = $this->_platform->getListViewsSQL($database); - $views = $this->_conn->fetchAllAssociative($sql); - - return $this->_getPortableViewsList($views); + return array_map(function (array $row): View { + return $this->_getPortableViewDefinition($row); + }, $this->connection->fetchAllAssociative( + $this->platform->getListViewsSQL( + $this->getDatabase(__METHOD__), + ), + )); } /** * Lists the foreign keys for the given table. * - * @param string $table The name of the table. - * @param string|null $database - * - * @return ForeignKeyConstraint[] + * @return array * * @throws Exception */ - public function listTableForeignKeys($table, $database = null) + public function listTableForeignKeys(string $table): array { - if ($database === null) { - $database = $this->getDatabase(__METHOD__); - } else { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5284', - 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', - ); - } - - $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); - $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); + $database = $this->getDatabase(__METHOD__); - return $this->_getPortableTableForeignKeysList($tableForeignKeys); + return $this->_getPortableTableForeignKeysList( + $this->selectForeignKeyColumns( + $database, + $this->normalizeName($table), + )->fetchAllAssociative(), + ); } /** - * @param string $table - * @param string|null $database - * - * @return ForeignKeyConstraint[] + * @return array * * @throws Exception */ - protected function doListTableForeignKeys($table, $database = null): array + private function getTableOptions(string $name): array { - if ($database === null) { - $database = $this->getDatabase(__METHOD__); - } else { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5284', - 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', - ); - } + $normalizedName = $this->normalizeName($name); - return $this->_getPortableTableForeignKeysList( - $this->selectForeignKeyColumns( - $database, - $this->normalizeName($table), - )->fetchAllAssociative(), - ); + return $this->fetchTableOptionsByTable( + $this->getDatabase(__METHOD__), + $normalizedName, + )[$normalizedName] ?? []; } /* drop*() Methods */ @@ -699,16 +391,12 @@ protected function doListTableForeignKeys($table, $database = null): array * * NOTE: You can not drop the database this SchemaManager is currently connected to. * - * @param string $database The name of the database to drop. - * - * @return void - * * @throws Exception */ - public function dropDatabase($database) + public function dropDatabase(string $database): void { - $this->_conn->executeStatement( - $this->_platform->getDropDatabaseSQL($database), + $this->connection->executeStatement( + $this->platform->getDropDatabaseSQL($database), ); } @@ -719,149 +407,56 @@ public function dropDatabase($database) */ public function dropSchema(string $schemaName): void { - $this->_conn->executeStatement( - $this->_platform->getDropSchemaSQL($schemaName), + $this->connection->executeStatement( + $this->platform->getDropSchemaSQL($schemaName), ); } /** * Drops the given table. * - * @param string $name The name of the table to drop. - * - * @return void - * * @throws Exception */ - public function dropTable($name) + public function dropTable(string $name): void { - $this->_conn->executeStatement( - $this->_platform->getDropTableSQL($name), + $this->connection->executeStatement( + $this->platform->getDropTableSQL($name), ); } /** * Drops the index from the given table. * - * @param Index|string $index The name of the index. - * @param Table|string $table The name of the table. - * - * @return void - * * @throws Exception */ - public function dropIndex($index, $table) + public function dropIndex(string $index, string $table): void { - if ($index instanceof Index) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $index = $index->getQuotedName($this->_platform); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this->_platform); - } - - $this->_conn->executeStatement( - $this->_platform->getDropIndexSQL($index, $table), + $this->connection->executeStatement( + $this->platform->getDropIndexSQL($index, $table), ); } - /** - * Drops the constraint from the given table. - * - * @deprecated Use {@see dropIndex()}, {@see dropForeignKey()} or {@see dropUniqueConstraint()} instead. - * - * @param Table|string $table The name of the table. - * - * @return void - * - * @throws Exception - */ - public function dropConstraint(Constraint $constraint, $table) - { - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this->_platform); - } - - $this->_conn->executeStatement($this->_platform->getDropConstraintSQL( - $constraint->getQuotedName($this->_platform), - $table, - )); - } - /** * Drops a foreign key from a table. * - * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key. - * @param Table|string $table The name of the table with the foreign key. - * - * @return void - * * @throws Exception */ - public function dropForeignKey($foreignKey, $table) + public function dropForeignKey(string $name, string $table): void { - if ($foreignKey instanceof ForeignKeyConstraint) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' - . ' Pass it as a quoted name instead.', - __METHOD__, - ); - - $foreignKey = $foreignKey->getQuotedName($this->_platform); - } - - if ($table instanceof Table) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4798', - 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', - __METHOD__, - ); - - $table = $table->getQuotedName($this->_platform); - } - - $this->_conn->executeStatement( - $this->_platform->getDropForeignKeySQL($foreignKey, $table), + $this->connection->executeStatement( + $this->platform->getDropForeignKeySQL($name, $table), ); } /** * Drops a sequence with a given name. * - * @param string $name The name of the sequence to drop. - * - * @return void - * * @throws Exception */ - public function dropSequence($name) + public function dropSequence(string $name): void { - $this->_conn->executeStatement( - $this->_platform->getDropSequenceSQL($name), + $this->connection->executeStatement( + $this->platform->getDropSequenceSQL($name), ); } @@ -872,24 +467,20 @@ public function dropSequence($name) */ public function dropUniqueConstraint(string $name, string $tableName): void { - $this->_conn->executeStatement( - $this->_platform->getDropUniqueConstraintSQL($name, $tableName), + $this->connection->executeStatement( + $this->platform->getDropUniqueConstraintSQL($name, $tableName), ); } /** * Drops a view. * - * @param string $name The name of the view. - * - * @return void - * * @throws Exception */ - public function dropView($name) + public function dropView(string $name): void { - $this->_conn->executeStatement( - $this->_platform->getDropViewSQL($name), + $this->connection->executeStatement( + $this->platform->getDropViewSQL($name), ); } @@ -898,312 +489,103 @@ public function dropView($name) /** @throws Exception */ public function createSchemaObjects(Schema $schema): void { - $this->_execSql($schema->toSql($this->_platform)); + $this->executeStatements($schema->toSql($this->platform)); } /** * Creates a new database. * - * @param string $database The name of the database to create. - * - * @return void - * * @throws Exception */ - public function createDatabase($database) + public function createDatabase(string $database): void { - $this->_conn->executeStatement( - $this->_platform->getCreateDatabaseSQL($database), + $this->connection->executeStatement( + $this->platform->getCreateDatabaseSQL($database), ); } /** * Creates a new table. * - * @return void - * * @throws Exception */ - public function createTable(Table $table) + public function createTable(Table $table): void { - $createFlags = AbstractPlatform::CREATE_INDEXES | AbstractPlatform::CREATE_FOREIGNKEYS; - $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); + $this->executeStatements($this->platform->getCreateTableSQL($table)); } /** * Creates a new sequence. * - * @param Sequence $sequence - * - * @return void - * * @throws Exception */ - public function createSequence($sequence) + public function createSequence(Sequence $sequence): void { - $this->_conn->executeStatement( - $this->_platform->getCreateSequenceSQL($sequence), + $this->connection->executeStatement( + $this->platform->getCreateSequenceSQL($sequence), ); } /** - * Creates a constraint on a table. - * - * @deprecated Use {@see createIndex()}, {@see createForeignKey()} or {@see createUniqueConstraint()} instead. - * - * @param Table|string $table + * Creates a new index on a table. * - * @return void + * @param string $table The name of the table on which the index is to be created. * * @throws Exception */ - public function createConstraint(Constraint $constraint, $table) + public function createIndex(Index $index, string $table): void { - $this->_conn->executeStatement( - $this->_platform->getCreateConstraintSQL($constraint, $table), + $this->connection->executeStatement( + $this->platform->getCreateIndexSQL($index, $table), ); } /** - * Creates a new index on a table. - * - * @param Table|string $table The name of the table on which the index is to be created. + * Creates a new foreign key. * - * @return void + * @param ForeignKeyConstraint $foreignKey The ForeignKey instance. + * @param string $table The name of the table on which the foreign key is to be created. * * @throws Exception */ - public function createIndex(Index $index, $table) + public function createForeignKey(ForeignKeyConstraint $foreignKey, string $table): void { - $this->_conn->executeStatement( - $this->_platform->getCreateIndexSQL($index, $table), + $this->connection->executeStatement( + $this->platform->getCreateForeignKeySQL($foreignKey, $table), ); } /** - * Creates a new foreign key. - * - * @param ForeignKeyConstraint $foreignKey The ForeignKey instance. - * @param Table|string $table The name of the table on which the foreign key is to be created. - * - * @return void - * - * @throws Exception - */ - public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) - { - $this->_conn->executeStatement( - $this->_platform->getCreateForeignKeySQL($foreignKey, $table), - ); - } - - /** - * Creates a unique constraint on a table. + * Creates a unique constraint on a table. * * @throws Exception */ public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void { - $this->_conn->executeStatement( - $this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), + $this->connection->executeStatement( + $this->platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), ); } /** * Creates a new view. * - * @return void - * * @throws Exception */ - public function createView(View $view) + public function createView(View $view): void { - $this->_conn->executeStatement( - $this->_platform->getCreateViewSQL( - $view->getQuotedName($this->_platform), + $this->connection->executeStatement( + $this->platform->getCreateViewSQL( + $view->getQuotedName($this->platform), $view->getSql(), ), ); } - /* dropAndCreate*() Methods */ - /** @throws Exception */ public function dropSchemaObjects(Schema $schema): void { - $this->_execSql($schema->toDropSql($this->_platform)); - } - - /** - * Drops and creates a constraint. - * - * @deprecated Use {@see dropIndex()} and {@see createIndex()}, - * {@see dropForeignKey()} and {@see createForeignKey()} - * or {@see dropUniqueConstraint()} and {@see createUniqueConstraint()} instead. - * - * @see dropConstraint() - * @see createConstraint() - * - * @param Table|string $table - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateConstraint(Constraint $constraint, $table) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateConstraint() is deprecated.' - . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex(),' - . ' AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey()' - . ' or AbstractSchemaManager::dropUniqueConstraint()' - . ' and AbstractSchemaManager::createUniqueConstraint() instead.', - ); - - $this->tryMethod('dropConstraint', $constraint, $table); - $this->createConstraint($constraint, $table); - } - - /** - * Drops and creates a new index on a table. - * - * @deprecated Use {@see dropIndex()} and {@see createIndex()} instead. - * - * @param Table|string $table The name of the table on which the index is to be created. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateIndex(Index $index, $table) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateIndex() is deprecated.' - . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex() instead.', - ); - - $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); - $this->createIndex($index, $table); - } - - /** - * Drops and creates a new foreign key. - * - * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. - * - * @param ForeignKeyConstraint $foreignKey An associative array that defines properties - * of the foreign key to be created. - * @param Table|string $table The name of the table on which the foreign key is to be created. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateForeignKey() is deprecated.' - . ' Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.', - ); - - $this->tryMethod('dropForeignKey', $foreignKey, $table); - $this->createForeignKey($foreignKey, $table); - } - - /** - * Drops and create a new sequence. - * - * @deprecated Use {@see dropSequence()} and {@see createSequence()} instead. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateSequence(Sequence $sequence) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateSequence() is deprecated.' - . ' Use AbstractSchemaManager::dropSequence() and AbstractSchemaManager::createSequence() instead.', - ); - - $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); - $this->createSequence($sequence); - } - - /** - * Drops and creates a new table. - * - * @deprecated Use {@see dropTable()} and {@see createTable()} instead. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateTable(Table $table) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateTable() is deprecated.' - . ' Use AbstractSchemaManager::dropTable() and AbstractSchemaManager::createTable() instead.', - ); - - $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); - $this->createTable($table); - } - - /** - * Drops and creates a new database. - * - * @deprecated Use {@see dropDatabase()} and {@see createDatabase()} instead. - * - * @param string $database The name of the database to create. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateDatabase($database) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateDatabase() is deprecated.' - . ' Use AbstractSchemaManager::dropDatabase() and AbstractSchemaManager::createDatabase() instead.', - ); - - $this->tryMethod('dropDatabase', $database); - $this->createDatabase($database); - } - - /** - * Drops and creates a new view. - * - * @deprecated Use {@see dropView()} and {@see createView()} instead. - * - * @return void - * - * @throws Exception - */ - public function dropAndCreateView(View $view) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'AbstractSchemaManager::dropAndCreateView() is deprecated.' - . ' Use AbstractSchemaManager::dropView() and AbstractSchemaManager::createView() instead.', - ); - - $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); - $this->createView($view); + $this->executeStatements($schema->toDropSql($this->platform)); } /** @@ -1213,7 +595,7 @@ public function dropAndCreateView(View $view) */ public function alterSchema(SchemaDiff $schemaDiff): void { - $this->_execSql($this->_platform->getAlterSchemaSQL($schemaDiff)); + $this->executeStatements($this->platform->getAlterSchemaSQL($schemaDiff)); } /** @@ -1221,10 +603,10 @@ public function alterSchema(SchemaDiff $schemaDiff): void * * @throws Exception */ - public function migrateSchema(Schema $toSchema): void + public function migrateSchema(Schema $newSchema): void { $schemaDiff = $this->createComparator() - ->compareSchemas($this->introspectSchema(), $toSchema); + ->compareSchemas($this->introspectSchema(), $newSchema); $this->alterSchema($schemaDiff); } @@ -1234,28 +616,23 @@ public function migrateSchema(Schema $toSchema): void /** * Alters an existing tables schema. * - * @return void - * * @throws Exception */ - public function alterTable(TableDiff $tableDiff) + public function alterTable(TableDiff $tableDiff): void { - $this->_execSql($this->_platform->getAlterTableSQL($tableDiff)); + $this->executeStatements($this->platform->getAlterTableSQL($tableDiff)); } /** * Renames a given table to another name. * - * @param string $name The current name of the table. - * @param string $newName The new name of the table. - * - * @return void - * * @throws Exception */ - public function renameTable($name, $newName) + public function renameTable(string $name, string $newName): void { - $this->_execSql($this->_platform->getRenameTableSQL($name, $newName)); + $this->connection->executeStatement( + $this->platform->getRenameTableSQL($name, $newName), + ); } /** @@ -1263,108 +640,16 @@ public function renameTable($name, $newName) * the native DBMS data definition to a portable Doctrine definition */ - /** - * @param mixed[] $databases - * - * @return string[] - */ - protected function _getPortableDatabasesList($databases) + /** @param array $database */ + protected function _getPortableDatabaseDefinition(array $database): string { - $list = []; - foreach ($databases as $value) { - $list[] = $this->_getPortableDatabaseDefinition($value); - } - - return $list; + throw NotSupported::new(__METHOD__); } - /** - * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. - * - * @deprecated Use {@see listSchemaNames()} instead. - * - * @param array> $namespaces The list of namespace names - * in the native DBMS data definition. - * - * @return string[] - */ - protected function getPortableNamespacesList(array $namespaces) + /** @param array $sequence */ + protected function _getPortableSequenceDefinition(array $sequence): Sequence { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.', - ); - - $namespacesList = []; - - foreach ($namespaces as $namespace) { - $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); - } - - return $namespacesList; - } - - /** - * @param mixed $database - * - * @return mixed - */ - protected function _getPortableDatabaseDefinition($database) - { - return $database; - } - - /** - * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. - * - * @deprecated Use {@see listSchemaNames()} instead. - * - * @param array $namespace The native DBMS namespace definition. - * - * @return mixed - */ - protected function getPortableNamespaceDefinition(array $namespace) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use AbstractSchemaManager::listSchemaNames() instead.', - ); - - return $namespace; - } - - /** - * @param mixed[][] $sequences - * - * @return Sequence[] - * - * @throws Exception - */ - protected function _getPortableSequencesList($sequences) - { - $list = []; - - foreach ($sequences as $value) { - $list[] = $this->_getPortableSequenceDefinition($value); - } - - return $list; - } - - /** - * @param mixed[] $sequence - * - * @return Sequence - * - * @throws Exception - */ - protected function _getPortableSequenceDefinition($sequence) - { - throw Exception::notSupported('Sequences'); + throw NotSupported::new(__METHOD__); } /** @@ -1372,47 +657,19 @@ protected function _getPortableSequenceDefinition($sequence) * * The name of the created column instance however is kept in its case. * - * @param string $table The name of the table. - * @param string $database - * @param mixed[][] $tableColumns + * @param array> $tableColumns * - * @return Column[] + * @return array * * @throws Exception */ - protected function _getPortableTableColumnList($table, $database, $tableColumns) + protected function _getPortableTableColumnList(string $table, string $database, array $tableColumns): array { - $eventManager = $this->_platform->getEventManager(); - $list = []; foreach ($tableColumns as $tableColumn) { - $column = null; - $defaultPrevented = false; - - if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', - Events::onSchemaColumnDefinition, - ); - - $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); - $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); - - $defaultPrevented = $eventArgs->isDefaultPrevented(); - $column = $eventArgs->getColumn(); - } - - if (! $defaultPrevented) { - $column = $this->_getPortableTableColumnDefinition($tableColumn); - } + $column = $this->_getPortableTableColumnDefinition($tableColumn); - if ($column === null) { - continue; - } - - $name = strtolower($column->getQuotedName($this->_platform)); + $name = strtolower($column->getQuotedName($this->platform)); $list[$name] = $column; } @@ -1422,25 +679,22 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) /** * Gets Table Column Definition. * - * @param mixed[] $tableColumn - * - * @return Column + * @param array $tableColumn * * @throws Exception */ - abstract protected function _getPortableTableColumnDefinition($tableColumn); + abstract protected function _getPortableTableColumnDefinition(array $tableColumn): Column; /** * Aggregates and groups the index results according to the required data result. * - * @param mixed[][] $tableIndexes - * @param string|null $tableName + * @param array> $tableIndexes * - * @return Index[] + * @return array * * @throws Exception */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { $result = []; foreach ($tableIndexes as $tableIndex) { @@ -1474,112 +728,33 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null; } - $eventManager = $this->_platform->getEventManager(); - $indexes = []; foreach ($result as $indexKey => $data) { - $index = null; - $defaultPrevented = false; - - if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/5784', - 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', - Events::onSchemaColumnDefinition, - ); - - $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); - $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); - - $defaultPrevented = $eventArgs->isDefaultPrevented(); - $index = $eventArgs->getIndex(); - } - - if (! $defaultPrevented) { - $index = new Index( - $data['name'], - $data['columns'], - $data['unique'], - $data['primary'], - $data['flags'], - $data['options'], - ); - } - - if ($index === null) { - continue; - } - - $indexes[$indexKey] = $index; + $indexes[$indexKey] = new Index( + $data['name'], + $data['columns'], + $data['unique'], + $data['primary'], + $data['flags'], + $data['options'], + ); } return $indexes; } - /** - * @param mixed[][] $tables - * - * @return string[] - */ - protected function _getPortableTablesList($tables) - { - $list = []; - foreach ($tables as $value) { - $list[] = $this->_getPortableTableDefinition($value); - } + /** @param array $table */ + abstract protected function _getPortableTableDefinition(array $table): string; - return $list; - } + /** @param array $view */ + abstract protected function _getPortableViewDefinition(array $view): View; /** - * @param mixed $table + * @param array> $tableForeignKeys * - * @return string + * @return array */ - protected function _getPortableTableDefinition($table) - { - return $table; - } - - /** - * @param mixed[][] $views - * - * @return View[] - */ - protected function _getPortableViewsList($views) - { - $list = []; - foreach ($views as $value) { - $view = $this->_getPortableViewDefinition($value); - - if ($view === false) { - continue; - } - - $viewName = strtolower($view->getQuotedName($this->_platform)); - $list[$viewName] = $view; - } - - return $list; - } - - /** - * @param mixed[] $view - * - * @return View|false - */ - protected function _getPortableViewDefinition($view) - { - return false; - } - - /** - * @param mixed[][] $tableForeignKeys - * - * @return ForeignKeyConstraint[] - */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $list = []; @@ -1590,61 +765,37 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) return $list; } - /** - * @param mixed $tableForeignKey - * - * @return ForeignKeyConstraint - * - * @abstract - */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) - { - return $tableForeignKey; - } + /** @param array $tableForeignKey */ + abstract protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint; /** - * @internal - * - * @param string[]|string $sql - * - * @return void + * @param array $sql * * @throws Exception */ - protected function _execSql($sql) + private function executeStatements(array $sql): void { - foreach ((array) $sql as $query) { - $this->_conn->executeStatement($query); + foreach ($sql as $query) { + $this->connection->executeStatement($query); } } /** - * Creates a schema instance for the current database. - * - * @deprecated Use {@link introspectSchema()} instead. - * - * @return Schema + * Returns a {@see Schema} instance representing the current database schema. * * @throws Exception */ - public function createSchema() + public function introspectSchema(): Schema { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5613', - '%s is deprecated. Use introspectSchema() instead.', - __METHOD__, - ); - $schemaNames = []; - if ($this->_platform->supportsSchemas()) { - $schemaNames = $this->listNamespaceNames(); + if ($this->platform->supportsSchemas()) { + $schemaNames = $this->listSchemaNames(); } $sequences = []; - if ($this->_platform->supportsSequences()) { + if ($this->platform->supportsSequences()) { $sequences = $this->listSequences(); } @@ -1653,34 +804,17 @@ public function createSchema() return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); } - /** - * Returns a {@see Schema} instance representing the current database schema. - * - * @throws Exception - */ - public function introspectSchema(): Schema - { - return $this->createSchema(); - } - /** * Creates the configuration for this schema. * - * @return SchemaConfig - * * @throws Exception */ - public function createSchemaConfig() + public function createSchemaConfig(): SchemaConfig { $schemaConfig = new SchemaConfig(); - $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); + $schemaConfig->setMaxIdentifierLength($this->platform->getMaxIdentifierLength()); - $searchPaths = $this->getSchemaSearchPaths(); - if (isset($searchPaths[0])) { - $schemaConfig->setName($searchPaths[0]); - } - - $params = $this->_conn->getParams(); + $params = $this->connection->getParams(); if (! isset($params['defaultTableOptions'])) { $params['defaultTableOptions'] = []; } @@ -1694,80 +828,10 @@ public function createSchemaConfig() return $schemaConfig; } - /** - * The search path for namespaces in the currently connected database. - * - * The first entry is usually the default namespace in the Schema. All - * further namespaces contain tables/sequences which can also be addressed - * with a short, not full-qualified name. - * - * For databases that don't support subschema/namespaces this method - * returns the name of the currently connected database. - * - * @deprecated - * - * @return string[] - * - * @throws Exception - */ - public function getSchemaSearchPaths() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4821', - 'AbstractSchemaManager::getSchemaSearchPaths() is deprecated.', - ); - - $database = $this->_conn->getDatabase(); - - if ($database !== null) { - return [$database]; - } - - return []; - } - - /** - * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns - * the type given as default. - * - * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. - * - * @param string|null $comment - * @param string $currentType - * - * @return string - */ - public function extractDoctrineTypeFromComment($comment, $currentType) - { - if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match) === 1) { - return $match[1]; - } - - return $currentType; - } - - /** - * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. - * - * @param string|null $comment - * @param string|null $type - * - * @return string|null - */ - public function removeDoctrineTypeFromComment($comment, $type) - { - if ($comment === null) { - return null; - } - - return str_replace('(DC2Type:' . $type . ')', '', $comment); - } - /** @throws Exception */ private function getDatabase(string $methodName): string { - $database = $this->_conn->getDatabase(); + $database = $this->connection->getDatabase(); if ($database === null) { throw DatabaseRequired::new($methodName); @@ -1778,7 +842,7 @@ private function getDatabase(string $methodName): string public function createComparator(): Comparator { - return new Comparator($this->_platform); + return new Comparator($this->platform); } /** diff --git a/doctrine/dbal/src/Schema/Column.php b/doctrine/dbal/src/Schema/Column.php index e15801786..8963cd7ac 100644 --- a/doctrine/dbal/src/Schema/Column.php +++ b/doctrine/dbal/src/Schema/Column.php @@ -1,13 +1,13 @@ */ + protected array $_platformOptions = []; - /** @var string|null */ - protected $_columnDefinition; + protected ?string $_columnDefinition = null; - /** @var string|null */ - protected $_comment; - - /** - * @deprecated Use {@link $_platformOptions} instead - * - * @var mixed[] - */ - protected $_customSchemaOptions = []; + protected string $_comment = ''; /** * Creates a new Column. * - * @param string $name - * @param mixed[] $options - * - * @throws SchemaException + * @param array $options */ - public function __construct($name, Type $type, array $options = []) + public function __construct(string $name, Type $type, array $options = []) { $this->_setName($name); $this->setType($type); $this->setOptions($options); } - /** - * @param mixed[] $options - * - * @return Column - * - * @throws SchemaException - */ - public function setOptions(array $options) + /** @param array $options */ + public function setOptions(array $options): self { foreach ($options as $name => $value) { $method = 'set' . $name; @@ -95,358 +68,171 @@ public function setOptions(array $options) return $this; } - /** @return Column */ - public function setType(Type $type) + public function setType(Type $type): self { $this->_type = $type; return $this; } - /** - * @param int|null $length - * - * @return Column - */ - public function setLength($length) + public function setLength(?int $length): self { - if ($length !== null) { - $this->_length = (int) $length; - } else { - $this->_length = null; - } + $this->_length = $length; return $this; } - /** - * @param int $precision - * - * @return Column - */ - public function setPrecision($precision) + public function setPrecision(?int $precision): self { - if (! is_numeric($precision)) { - $precision = 10; // defaults to 10 when no valid precision is given. - } - - $this->_precision = (int) $precision; + $this->_precision = $precision; return $this; } - /** - * @param int $scale - * - * @return Column - */ - public function setScale($scale) + public function setScale(int $scale): self { - if (! is_numeric($scale)) { - $scale = 0; - } - - $this->_scale = (int) $scale; + $this->_scale = $scale; return $this; } - /** - * @param bool $unsigned - * - * @return Column - */ - public function setUnsigned($unsigned) + public function setUnsigned(bool $unsigned): self { - $this->_unsigned = (bool) $unsigned; + $this->_unsigned = $unsigned; return $this; } - /** - * @param bool $fixed - * - * @return Column - */ - public function setFixed($fixed) + public function setFixed(bool $fixed): self { - $this->_fixed = (bool) $fixed; + $this->_fixed = $fixed; return $this; } - /** - * @param bool $notnull - * - * @return Column - */ - public function setNotnull($notnull) + public function setNotnull(bool $notnull): self { - $this->_notnull = (bool) $notnull; + $this->_notnull = $notnull; return $this; } - /** - * @param mixed $default - * - * @return Column - */ - public function setDefault($default) + public function setDefault(mixed $default): self { $this->_default = $default; return $this; } - /** - * @param mixed[] $platformOptions - * - * @return Column - */ - public function setPlatformOptions(array $platformOptions) + /** @param array $platformOptions */ + public function setPlatformOptions(array $platformOptions): self { $this->_platformOptions = $platformOptions; return $this; } - /** - * @param string $name - * @param mixed $value - * - * @return Column - */ - public function setPlatformOption($name, $value) + public function setPlatformOption(string $name, mixed $value): self { $this->_platformOptions[$name] = $value; return $this; } - /** - * @param string|null $value - * - * @return Column - */ - public function setColumnDefinition($value) + public function setColumnDefinition(?string $value): self { $this->_columnDefinition = $value; return $this; } - /** @return Type */ - public function getType() + public function getType(): Type { return $this->_type; } - /** @return int|null */ - public function getLength() + public function getLength(): ?int { return $this->_length; } - /** @return int */ - public function getPrecision() + public function getPrecision(): ?int { return $this->_precision; } - /** @return int */ - public function getScale() + public function getScale(): int { return $this->_scale; } - /** @return bool */ - public function getUnsigned() + public function getUnsigned(): bool { return $this->_unsigned; } - /** @return bool */ - public function getFixed() + public function getFixed(): bool { return $this->_fixed; } - /** @return bool */ - public function getNotnull() + public function getNotnull(): bool { return $this->_notnull; } - /** @return mixed */ - public function getDefault() + public function getDefault(): mixed { return $this->_default; } - /** @return mixed[] */ - public function getPlatformOptions() + /** @return array */ + public function getPlatformOptions(): array { return $this->_platformOptions; } - /** - * @param string $name - * - * @return bool - */ - public function hasPlatformOption($name) + public function hasPlatformOption(string $name): bool { return isset($this->_platformOptions[$name]); } - /** - * @param string $name - * - * @return mixed - */ - public function getPlatformOption($name) + public function getPlatformOption(string $name): mixed { return $this->_platformOptions[$name]; } - /** @return string|null */ - public function getColumnDefinition() + public function getColumnDefinition(): ?string { return $this->_columnDefinition; } - /** @return bool */ - public function getAutoincrement() + public function getAutoincrement(): bool { return $this->_autoincrement; } - /** - * @param bool $flag - * - * @return Column - */ - public function setAutoincrement($flag) + public function setAutoincrement(bool $flag): self { $this->_autoincrement = $flag; return $this; } - /** - * @param string|null $comment - * - * @return Column - */ - public function setComment($comment) + public function setComment(string $comment): self { $this->_comment = $comment; return $this; } - /** @return string|null */ - public function getComment() + public function getComment(): string { return $this->_comment; } - /** - * @deprecated Use {@link setPlatformOption()} instead - * - * @param string $name - * @param mixed $value - * - * @return Column - */ - public function setCustomSchemaOption($name, $value) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5476', - 'Column::setCustomSchemaOption() is deprecated. Use setPlatformOption() instead.', - ); - - $this->_customSchemaOptions[$name] = $value; - - return $this; - } - - /** - * @deprecated Use {@link hasPlatformOption()} instead - * - * @param string $name - * - * @return bool - */ - public function hasCustomSchemaOption($name) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5476', - 'Column::hasCustomSchemaOption() is deprecated. Use hasPlatformOption() instead.', - ); - - return isset($this->_customSchemaOptions[$name]); - } - - /** - * @deprecated Use {@link getPlatformOption()} instead - * - * @param string $name - * - * @return mixed - */ - public function getCustomSchemaOption($name) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5476', - 'Column::getCustomSchemaOption() is deprecated. Use getPlatformOption() instead.', - ); - - return $this->_customSchemaOptions[$name]; - } - - /** - * @deprecated Use {@link setPlatformOptions()} instead - * - * @param mixed[] $customSchemaOptions - * - * @return Column - */ - public function setCustomSchemaOptions(array $customSchemaOptions) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5476', - 'Column::setCustomSchemaOptions() is deprecated. Use setPlatformOptions() instead.', - ); - - $this->_customSchemaOptions = $customSchemaOptions; - - return $this; - } - - /** - * @deprecated Use {@link getPlatformOptions()} instead - * - * @return mixed[] - */ - public function getCustomSchemaOptions() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5476', - 'Column::getCustomSchemaOptions() is deprecated. Use getPlatformOptions() instead.', - ); - - return $this->_customSchemaOptions; - } - - /** @return mixed[] */ - public function toArray() + /** @return array */ + public function toArray(): array { return array_merge([ 'name' => $this->_name, @@ -461,6 +247,6 @@ public function toArray() 'autoincrement' => $this->_autoincrement, 'columnDefinition' => $this->_columnDefinition, 'comment' => $this->_comment, - ], $this->_platformOptions, $this->_customSchemaOptions); + ], $this->_platformOptions); } } diff --git a/doctrine/dbal/src/Schema/ColumnDiff.php b/doctrine/dbal/src/Schema/ColumnDiff.php index bd1b0eee0..3e4950a6a 100644 --- a/doctrine/dbal/src/Schema/ColumnDiff.php +++ b/doctrine/dbal/src/Schema/ColumnDiff.php @@ -1,169 +1,106 @@ oldColumnName = $oldColumnName; - $this->column = $column; - $this->changedProperties = $changedProperties; - $this->fromColumn = $fromColumn; + /** @internal The diff can be only instantiated by a {@see Comparator}. */ + public function __construct(private readonly Column $oldColumn, private readonly Column $newColumn) + { } - public function getOldColumn(): ?Column + public function getOldColumn(): Column { - return $this->fromColumn; + return $this->oldColumn; } public function getNewColumn(): Column { - return $this->column; + return $this->newColumn; } public function hasTypeChanged(): bool { - return $this->hasChanged('type'); + return $this->newColumn->getType()::class !== $this->oldColumn->getType()::class; } public function hasLengthChanged(): bool { - return $this->hasChanged('length'); + return $this->hasPropertyChanged(static function (Column $column): ?int { + return $column->getLength(); + }); } public function hasPrecisionChanged(): bool { - return $this->hasChanged('precision'); + return $this->hasPropertyChanged(static function (Column $column): ?int { + return $column->getPrecision(); + }); } public function hasScaleChanged(): bool { - return $this->hasChanged('scale'); + return $this->hasPropertyChanged(static function (Column $column): int { + return $column->getScale(); + }); } public function hasUnsignedChanged(): bool { - return $this->hasChanged('unsigned'); + return $this->hasPropertyChanged(static function (Column $column): bool { + return $column->getUnsigned(); + }); } public function hasFixedChanged(): bool { - return $this->hasChanged('fixed'); + return $this->hasPropertyChanged(static function (Column $column): bool { + return $column->getFixed(); + }); } public function hasNotNullChanged(): bool { - return $this->hasChanged('notnull'); + return $this->hasPropertyChanged(static function (Column $column): bool { + return $column->getNotnull(); + }); } public function hasDefaultChanged(): bool { - return $this->hasChanged('default'); + $oldDefault = $this->oldColumn->getDefault(); + $newDefault = $this->newColumn->getDefault(); + + // Null values need to be checked additionally as they tell whether to create or drop a default value. + // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. + if (($newDefault === null) xor ($oldDefault === null)) { + return true; + } + + return $newDefault != $oldDefault; } public function hasAutoIncrementChanged(): bool { - return $this->hasChanged('autoincrement'); + return $this->hasPropertyChanged(static function (Column $column): bool { + return $column->getAutoincrement(); + }); } public function hasCommentChanged(): bool { - return $this->hasChanged('comment'); + return $this->hasPropertyChanged(static function (Column $column): string { + return $column->getComment(); + }); } - /** - * @deprecated Use {@see hasTypeChanged()}, {@see hasLengthChanged()}, {@see hasPrecisionChanged()}, - * {@see hasScaleChanged()}, {@see hasUnsignedChanged()}, {@see hasFixedChanged()}, {@see hasNotNullChanged()}, - * {@see hasDefaultChanged()}, {@see hasAutoIncrementChanged()} or {@see hasCommentChanged()} instead. - * - * @param string $propertyName - * - * @return bool - */ - public function hasChanged($propertyName) + private function hasPropertyChanged(callable $property): bool { - return in_array($propertyName, $this->changedProperties, true); - } - - /** - * @deprecated Use {@see $fromColumn} instead. - * - * @return Identifier - */ - public function getOldColumnName() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5622', - '%s is deprecated. Use $fromColumn instead.', - __METHOD__, - ); - - if ($this->fromColumn !== null) { - $name = $this->fromColumn->getName(); - $quote = $this->fromColumn->isQuoted(); - } else { - $name = $this->oldColumnName; - $quote = false; - } - - return new Identifier($name, $quote); + return $property($this->newColumn) !== $property($this->oldColumn); } } diff --git a/doctrine/dbal/src/Schema/Comparator.php b/doctrine/dbal/src/Schema/Comparator.php index 28e7f2f73..4c60c0770 100644 --- a/doctrine/dbal/src/Schema/Comparator.php +++ b/doctrine/dbal/src/Schema/Comparator.php @@ -1,93 +1,31 @@ platform = $platform; - } - - /** @param list $args */ - public function __call(string $method, array $args): SchemaDiff - { - if ($method !== 'compareSchemas') { - throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); - } - - return $this->doCompareSchemas(...$args); - } - - /** @param list $args */ - public static function __callStatic(string $method, array $args): SchemaDiff + public function __construct(private readonly AbstractPlatform $platform) { - if ($method !== 'compareSchemas') { - throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); - } - - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4707', - 'Calling %s::%s() statically is deprecated.', - self::class, - $method, - ); - - $comparator = new self(); - - return $comparator->doCompareSchemas(...$args); } /** - * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. - * - * This method should be called non-statically since it will be declared as non-static in the next major release. - * - * @return SchemaDiff - * - * @throws SchemaException + * Returns the differences between the schemas. */ - private function doCompareSchemas( - Schema $fromSchema, - Schema $toSchema - ) { + public function compareSchemas(Schema $oldSchema, Schema $newSchema): SchemaDiff + { $createdSchemas = []; $droppedSchemas = []; $createdTables = []; @@ -97,164 +35,90 @@ private function doCompareSchemas( $alteredSequences = []; $droppedSequences = []; - $orphanedForeignKeys = []; - - $foreignKeysToTable = []; - - foreach ($toSchema->getNamespaces() as $namespace) { - if ($fromSchema->hasNamespace($namespace)) { + foreach ($newSchema->getNamespaces() as $newNamespace) { + if ($oldSchema->hasNamespace($newNamespace)) { continue; } - $createdSchemas[$namespace] = $namespace; + $createdSchemas[] = $newNamespace; } - foreach ($fromSchema->getNamespaces() as $namespace) { - if ($toSchema->hasNamespace($namespace)) { + foreach ($oldSchema->getNamespaces() as $oldNamespace) { + if ($newSchema->hasNamespace($oldNamespace)) { continue; } - $droppedSchemas[$namespace] = $namespace; + $droppedSchemas[] = $oldNamespace; } - foreach ($toSchema->getTables() as $table) { - $tableName = $table->getShortestName($toSchema->getName()); - if (! $fromSchema->hasTable($tableName)) { - $createdTables[$tableName] = $toSchema->getTable($tableName); + foreach ($newSchema->getTables() as $newTable) { + $newTableName = $newTable->getShortestName($newSchema->getName()); + if (! $oldSchema->hasTable($newTableName)) { + $createdTables[] = $newSchema->getTable($newTableName); } else { - $tableDifferences = $this->diffTable( - $fromSchema->getTable($tableName), - $toSchema->getTable($tableName), + $tableDiff = $this->compareTables( + $oldSchema->getTable($newTableName), + $newSchema->getTable($newTableName), ); - if ($tableDifferences !== false) { - $alteredTables[$tableName] = $tableDifferences; + if (! $tableDiff->isEmpty()) { + $alteredTables[] = $tableDiff; } } } - /* Check if there are tables removed */ - foreach ($fromSchema->getTables() as $table) { - $tableName = $table->getShortestName($fromSchema->getName()); - - $table = $fromSchema->getTable($tableName); - if (! $toSchema->hasTable($tableName)) { - $droppedTables[$tableName] = $table; - } - - // also remember all foreign keys that point to a specific table - foreach ($table->getForeignKeys() as $foreignKey) { - $foreignTable = strtolower($foreignKey->getForeignTableName()); - if (! isset($foreignKeysToTable[$foreignTable])) { - $foreignKeysToTable[$foreignTable] = []; - } + // Check if there are tables removed + foreach ($oldSchema->getTables() as $oldTable) { + $oldTableName = $oldTable->getShortestName($oldSchema->getName()); - $foreignKeysToTable[$foreignTable][] = $foreignKey; - } - } - - foreach ($droppedTables as $tableName => $table) { - if (! isset($foreignKeysToTable[$tableName])) { + $oldTable = $oldSchema->getTable($oldTableName); + if ($newSchema->hasTable($oldTableName)) { continue; } - foreach ($foreignKeysToTable[$tableName] as $foreignKey) { - if (isset($droppedTables[strtolower($foreignKey->getLocalTableName())])) { - continue; - } - - $orphanedForeignKeys[] = $foreignKey; - } - - // deleting duplicated foreign keys present on both on the orphanedForeignKey - // and the removedForeignKeys from changedTables - foreach ($foreignKeysToTable[$tableName] as $foreignKey) { - // strtolower the table name to make if compatible with getShortestName - $localTableName = strtolower($foreignKey->getLocalTableName()); - if (! isset($alteredTables[$localTableName])) { - continue; - } - - foreach ($alteredTables[$localTableName]->getDroppedForeignKeys() as $droppedForeignKey) { - assert($droppedForeignKey instanceof ForeignKeyConstraint); - - // We check if the key is from the removed table if not we skip. - if ($tableName !== strtolower($droppedForeignKey->getForeignTableName())) { - continue; - } - - $alteredTables[$localTableName]->unsetDroppedForeignKey($droppedForeignKey); - } - } + $droppedTables[] = $oldTable; } - foreach ($toSchema->getSequences() as $sequence) { - $sequenceName = $sequence->getShortestName($toSchema->getName()); - if (! $fromSchema->hasSequence($sequenceName)) { - if (! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { - $createdSequences[] = $sequence; + foreach ($newSchema->getSequences() as $newSequence) { + $newSequenceName = $newSequence->getShortestName($newSchema->getName()); + if (! $oldSchema->hasSequence($newSequenceName)) { + if (! $this->isAutoIncrementSequenceInSchema($oldSchema, $newSequence)) { + $createdSequences[] = $newSequence; } } else { - if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { - $alteredSequences[] = $toSchema->getSequence($sequenceName); + if ($this->diffSequence($newSequence, $oldSchema->getSequence($newSequenceName))) { + $alteredSequences[] = $newSchema->getSequence($newSequenceName); } } } - foreach ($fromSchema->getSequences() as $sequence) { - if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { + foreach ($oldSchema->getSequences() as $oldSequence) { + if ($this->isAutoIncrementSequenceInSchema($newSchema, $oldSequence)) { continue; } - $sequenceName = $sequence->getShortestName($fromSchema->getName()); + $oldSequenceName = $oldSequence->getShortestName($oldSchema->getName()); - if ($toSchema->hasSequence($sequenceName)) { + if ($newSchema->hasSequence($oldSequenceName)) { continue; } - $droppedSequences[] = $sequence; + $droppedSequences[] = $oldSequence; } - $diff = new SchemaDiff( + return new SchemaDiff( + $createdSchemas, + $droppedSchemas, $createdTables, $alteredTables, $droppedTables, - $fromSchema, - $createdSchemas, - $droppedSchemas, $createdSequences, $alteredSequences, $droppedSequences, ); - - $diff->orphanedForeignKeys = $orphanedForeignKeys; - - return $diff; - } - - /** - * @deprecated Use non-static call to {@see compareSchemas()} instead. - * - * @return SchemaDiff - * - * @throws SchemaException - */ - public function compare(Schema $fromSchema, Schema $toSchema) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4707', - 'Method compare() is deprecated. Use a non-static call to compareSchemas() instead.', - ); - - return $this->compareSchemas($fromSchema, $toSchema); } - /** - * @param Schema $schema - * @param Sequence $sequence - */ - private function isAutoIncrementSequenceInSchema($schema, $sequence): bool + private function isAutoIncrementSequenceInSchema(Schema $schema, Sequence $sequence): bool { foreach ($schema->getTables() as $table) { if ($sequence->isAutoIncrementsFor($table)) { @@ -265,8 +129,7 @@ private function isAutoIncrementSequenceInSchema($schema, $sequence): bool return false; } - /** @return bool */ - public function diffSequence(Sequence $sequence1, Sequence $sequence2) + public function diffSequence(Sequence $sequence1, Sequence $sequence2): bool { if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { return true; @@ -275,41 +138,10 @@ public function diffSequence(Sequence $sequence1, Sequence $sequence2) return $sequence1->getInitialValue() !== $sequence2->getInitialValue(); } - /** - * Returns the difference between the tables $fromTable and $toTable. - * - * If there are no differences this method returns the boolean false. - * - * @deprecated Use {@see compareTables()} and, optionally, {@see TableDiff::isEmpty()} instead. - * - * @return TableDiff|false - * - * @throws Exception - */ - public function diffTable(Table $fromTable, Table $toTable) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5770', - '%s is deprecated. Use compareTables() instead.', - __METHOD__, - ); - - $diff = $this->compareTables($fromTable, $toTable); - - if ($diff->isEmpty()) { - return false; - } - - return $diff; - } - /** * Compares the tables and returns the difference between them. - * - * @throws Exception */ - public function compareTables(Table $fromTable, Table $toTable): TableDiff + public function compareTables(Table $oldTable, Table $newTable): TableDiff { $addedColumns = []; $modifiedColumns = []; @@ -321,126 +153,117 @@ public function compareTables(Table $fromTable, Table $toTable): TableDiff $modifiedForeignKeys = []; $droppedForeignKeys = []; - $fromTableColumns = $fromTable->getColumns(); - $toTableColumns = $toTable->getColumns(); + $oldColumns = $oldTable->getColumns(); + $newColumns = $newTable->getColumns(); + + // See if all the columns in the old table exist in the new table + foreach ($newColumns as $newColumn) { + $newColumnName = strtolower($newColumn->getName()); - /* See if all the columns in "from" table exist in "to" table */ - foreach ($toTableColumns as $columnName => $column) { - if ($fromTable->hasColumn($columnName)) { + if ($oldTable->hasColumn($newColumnName)) { continue; } - $addedColumns[$columnName] = $column; + $addedColumns[$newColumnName] = $newColumn; } - /* See if there are any removed columns in "to" table */ - foreach ($fromTableColumns as $columnName => $column) { - // See if column is removed in "to" table. - if (! $toTable->hasColumn($columnName)) { - $droppedColumns[$columnName] = $column; + // See if there are any removed columns in the new table + foreach ($oldColumns as $oldColumn) { + $oldColumnName = strtolower($oldColumn->getName()); + + // See if column is removed in the new table. + if (! $newTable->hasColumn($oldColumnName)) { + $droppedColumns[$oldColumnName] = $oldColumn; continue; } - $toColumn = $toTable->getColumn($columnName); - - // See if column has changed properties in "to" table. - $changedProperties = $this->diffColumn($column, $toColumn); + $newColumn = $newTable->getColumn($oldColumnName); - if ($this->platform !== null) { - if ($this->columnsEqual($column, $toColumn)) { - continue; - } - } elseif (count($changedProperties) === 0) { + if ($this->columnsEqual($oldColumn, $newColumn)) { continue; } - $modifiedColumns[$column->getName()] = new ColumnDiff( - $column->getName(), - $toColumn, - $changedProperties, - $column, - ); + $modifiedColumns[] = new ColumnDiff($oldColumn, $newColumn); } $renamedColumns = $this->detectRenamedColumns($addedColumns, $droppedColumns); - $fromTableIndexes = $fromTable->getIndexes(); - $toTableIndexes = $toTable->getIndexes(); + $oldIndexes = $oldTable->getIndexes(); + $newIndexes = $newTable->getIndexes(); - /* See if all the indexes in "from" table exist in "to" table */ - foreach ($toTableIndexes as $indexName => $index) { - if (($index->isPrimary() && $fromTable->getPrimaryKey() !== null) || $fromTable->hasIndex($indexName)) { + // See if all the indexes from the old table exist in the new one + foreach ($newIndexes as $newIndexName => $newIndex) { + if (($newIndex->isPrimary() && $oldTable->getPrimaryKey() !== null) || $oldTable->hasIndex($newIndexName)) { continue; } - $addedIndexes[$indexName] = $index; + $addedIndexes[$newIndexName] = $newIndex; } - /* See if there are any removed indexes in "to" table */ - foreach ($fromTableIndexes as $indexName => $index) { - // See if index is removed in "to" table. + // See if there are any removed indexes in the new table + foreach ($oldIndexes as $oldIndexName => $oldIndex) { + // See if the index is removed in the new table. if ( - ($index->isPrimary() && $toTable->getPrimaryKey() === null) || - ! $index->isPrimary() && ! $toTable->hasIndex($indexName) + ($oldIndex->isPrimary() && $newTable->getPrimaryKey() === null) || + ! $oldIndex->isPrimary() && ! $newTable->hasIndex($oldIndexName) ) { - $droppedIndexes[$indexName] = $index; + $droppedIndexes[$oldIndexName] = $oldIndex; continue; } - // See if index has changed in "to" table. - $toTableIndex = $index->isPrimary() ? $toTable->getPrimaryKey() : $toTable->getIndex($indexName); - assert($toTableIndex instanceof Index); + // See if index has changed in the new table. + $newIndex = $oldIndex->isPrimary() ? $newTable->getPrimaryKey() : $newTable->getIndex($oldIndexName); + assert($newIndex instanceof Index); - if (! $this->diffIndex($index, $toTableIndex)) { + if (! $this->diffIndex($oldIndex, $newIndex)) { continue; } - $modifiedIndexes[$indexName] = $toTableIndex; + $modifiedIndexes[] = $newIndex; } $renamedIndexes = $this->detectRenamedIndexes($addedIndexes, $droppedIndexes); - $fromForeignKeys = $fromTable->getForeignKeys(); - $toForeignKeys = $toTable->getForeignKeys(); + $oldForeignKeys = $oldTable->getForeignKeys(); + $newForeignKeys = $newTable->getForeignKeys(); - foreach ($fromForeignKeys as $fromKey => $fromConstraint) { - foreach ($toForeignKeys as $toKey => $toConstraint) { - if ($this->diffForeignKey($fromConstraint, $toConstraint) === false) { - unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); + foreach ($oldForeignKeys as $oldKey => $oldForeignKey) { + foreach ($newForeignKeys as $newKey => $newForeignKey) { + if ($this->diffForeignKey($oldForeignKey, $newForeignKey) === false) { + unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); } else { - if (strtolower($fromConstraint->getName()) === strtolower($toConstraint->getName())) { - $modifiedForeignKeys[] = $toConstraint; + if (strtolower($oldForeignKey->getName()) === strtolower($newForeignKey->getName())) { + $modifiedForeignKeys[] = $newForeignKey; - unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); + unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); } } } } - foreach ($fromForeignKeys as $fromConstraint) { - $droppedForeignKeys[] = $fromConstraint; + foreach ($oldForeignKeys as $oldForeignKey) { + $droppedForeignKeys[] = $oldForeignKey; } - foreach ($toForeignKeys as $toConstraint) { - $addedForeignKeys[] = $toConstraint; + foreach ($newForeignKeys as $newForeignKey) { + $addedForeignKeys[] = $newForeignKey; } return new TableDiff( - $toTable->getName(), + $oldTable, $addedColumns, $modifiedColumns, $droppedColumns, + $renamedColumns, $addedIndexes, $modifiedIndexes, $droppedIndexes, - $fromTable, + $renamedIndexes, $addedForeignKeys, $modifiedForeignKeys, $droppedForeignKeys, - $renamedColumns, - $renamedIndexes, ); } @@ -452,8 +275,6 @@ public function compareTables(Table $fromTable, Table $toTable): TableDiff * @param array $removedColumns * * @return array - * - * @throws Exception */ private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array { @@ -548,12 +369,7 @@ private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndex return $renamedIndexes; } - /** - * @internal The method should be only used from within the {@see Comparator} class hierarchy. - * - * @return bool - */ - public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) + protected function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2): bool { if ( array_map('strtolower', $key1->getUnquotedLocalColumns()) @@ -582,134 +398,19 @@ public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint /** * Compares the definitions of the given columns - * - * @internal The method should be only used from within the {@see Comparator} class hierarchy. - * - * @throws Exception */ - public function columnsEqual(Column $column1, Column $column2): bool + protected function columnsEqual(Column $column1, Column $column2): bool { - if ($this->platform === null) { - return $this->diffColumn($column1, $column2) === []; - } - return $this->platform->columnsEqual($column1, $column2); } - /** - * Returns the difference between the columns - * - * If there are differences this method returns the changed properties as a - * string array, otherwise an empty array gets returned. - * - * @deprecated Use {@see columnsEqual()} instead. - * - * @return string[] - */ - public function diffColumn(Column $column1, Column $column2) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5650', - '%s is deprecated. Use diffTable() instead.', - __METHOD__, - ); - - $properties1 = $column1->toArray(); - $properties2 = $column2->toArray(); - - $changedProperties = []; - - if (get_class($properties1['type']) !== get_class($properties2['type'])) { - $changedProperties[] = 'type'; - } - - foreach (['notnull', 'unsigned', 'autoincrement'] as $property) { - if ($properties1[$property] === $properties2[$property]) { - continue; - } - - $changedProperties[] = $property; - } - - // Null values need to be checked additionally as they tell whether to create or drop a default value. - // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. - if ( - ($properties1['default'] === null) !== ($properties2['default'] === null) - || $properties1['default'] != $properties2['default'] - ) { - $changedProperties[] = 'default'; - } - - if ( - ($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) || - $properties1['type'] instanceof Types\BinaryType - ) { - // check if value of length is set at all, default value assumed otherwise. - $length1 = $properties1['length'] ?? 255; - $length2 = $properties2['length'] ?? 255; - if ($length1 !== $length2) { - $changedProperties[] = 'length'; - } - - if ($properties1['fixed'] !== $properties2['fixed']) { - $changedProperties[] = 'fixed'; - } - } elseif ($properties1['type'] instanceof Types\DecimalType) { - if (($properties1['precision'] ?? 10) !== ($properties2['precision'] ?? 10)) { - $changedProperties[] = 'precision'; - } - - if ($properties1['scale'] !== $properties2['scale']) { - $changedProperties[] = 'scale'; - } - } - - // A null value and an empty string are actually equal for a comment so they should not trigger a change. - if ( - $properties1['comment'] !== $properties2['comment'] && - ! ($properties1['comment'] === null && $properties2['comment'] === '') && - ! ($properties2['comment'] === null && $properties1['comment'] === '') - ) { - $changedProperties[] = 'comment'; - } - - $customOptions1 = $column1->getCustomSchemaOptions(); - $customOptions2 = $column2->getCustomSchemaOptions(); - - foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) { - if (! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) { - $changedProperties[] = $key; - } elseif ($properties1[$key] !== $properties2[$key]) { - $changedProperties[] = $key; - } - } - - $platformOptions1 = $column1->getPlatformOptions(); - $platformOptions2 = $column2->getPlatformOptions(); - - foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) { - if ($properties1[$key] === $properties2[$key]) { - continue; - } - - $changedProperties[] = $key; - } - - return array_unique($changedProperties); - } - /** * Finds the difference between the indexes $index1 and $index2. * * Compares $index1 with $index2 and returns true if there are any * differences or false in case there are no differences. - * - * @internal The method should be only used from within the {@see Comparator} class hierarchy. - * - * @return bool */ - public function diffIndex(Index $index1, Index $index2) + protected function diffIndex(Index $index1, Index $index2): bool { return ! ($index1->isFulfilledBy($index2) && $index2->isFulfilledBy($index1)); } diff --git a/doctrine/dbal/src/Schema/Constraint.php b/doctrine/dbal/src/Schema/Constraint.php deleted file mode 100644 index f47ee1fd1..000000000 --- a/doctrine/dbal/src/Schema/Constraint.php +++ /dev/null @@ -1,41 +0,0 @@ -doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } - - /** - * {@inheritDoc} - */ - public function listTableForeignKeys($table, $database = null) - { - return $this->doListTableForeignKeys($table, $database); - } - /** * {@inheritDoc} * * @throws Exception */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); - $length = null; - $fixed = null; - $scale = false; - $precision = false; - - $default = null; + $length = $precision = $default = null; + $scale = 0; + $fixed = false; if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') { $default = $tableColumn['default']; @@ -108,12 +49,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } } - $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); - - if (isset($tableColumn['comment'])) { - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); - } + $type = $this->platform->getDoctrineTypeMapping($tableColumn['typename']); switch (strtolower($tableColumn['typename'])) { case 'varchar': @@ -122,7 +58,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) } $length = $tableColumn['length']; - $fixed = false; break; case 'character': @@ -147,16 +82,19 @@ protected function _getPortableTableColumnDefinition($tableColumn) } $options = [ - 'length' => $length, - 'fixed' => (bool) $fixed, - 'default' => $default, - 'autoincrement' => (bool) $tableColumn['autoincrement'], - 'notnull' => $tableColumn['nulls'] === 'N', - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, + 'length' => $length, + 'unsigned' => false, + 'fixed' => $fixed, + 'default' => $default, + 'autoincrement' => (bool) $tableColumn['autoincrement'], + 'notnull' => $tableColumn['nulls'] === 'N', + 'platformOptions' => [], ]; + if (isset($tableColumn['comment'])) { + $options['comment'] = $tableColumn['comment']; + } + if ($scale !== null && $precision !== null) { $options['scale'] = $scale; $options['precision'] = $precision; @@ -168,7 +106,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableTableDefinition(array $table): string { $table = array_change_key_case($table, CASE_LOWER); @@ -178,7 +116,7 @@ protected function _getPortableTableDefinition($table) /** * {@inheritDoc} */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { foreach ($tableIndexes as &$tableIndexRow) { $tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER); @@ -191,7 +129,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local_columns'], @@ -205,7 +143,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $foreignKeys = []; @@ -232,28 +170,10 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) return parent::_getPortableTableForeignKeysList($foreignKeys); } - /** - * @param string $def - * - * @return string|null - */ - protected function _getPortableForeignKeyRuleDef($def) - { - if ($def === 'C') { - return 'CASCADE'; - } - - if ($def === 'N') { - return 'SET NULL'; - } - - return null; - } - /** * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { $view = array_change_key_case($view, CASE_LOWER); @@ -283,7 +203,7 @@ protected function selectTableNames(string $databaseName): Result AND CREATOR = ? SQL; - return $this->_conn->executeQuery($sql, [$databaseName]); + return $this->connection->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result @@ -323,7 +243,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.TABNAME, C.COLNO'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -362,7 +282,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IDX.INDNAME, IDXCOL.COLSEQ'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -411,7 +331,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY R.CONSTNAME, FKCOL.COLSEQ'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } /** @@ -436,7 +356,7 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table } /** @var array> $metadata */ - $metadata = $this->_conn->executeQuery($sql, $params) + $metadata = $this->connection->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; diff --git a/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php index cc7acea03..53daac907 100644 --- a/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php +++ b/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class ColumnAlreadyExists extends SchemaException +final class ColumnAlreadyExists extends LogicException implements SchemaException { public static function new(string $tableName, string $columnName): self { - return new self( - sprintf('The column "%s" on table "%s" already exists.', $columnName, $tableName), - self::COLUMN_ALREADY_EXISTS, - ); + return new self(sprintf('The column "%s" on table "%s" already exists.', $columnName, $tableName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php index c9036a07c..cb1cedf62 100644 --- a/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class ColumnDoesNotExist extends SchemaException +final class ColumnDoesNotExist extends LogicException implements SchemaException { public static function new(string $columnName, string $table): self { - return new self( - sprintf('There is no column with name "%s" on table "%s".', $columnName, $table), - self::COLUMN_DOESNT_EXIST, - ); + return new self(sprintf('There is no column with name "%s" on table "%s".', $columnName, $table)); } } diff --git a/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php index bf57cd4ff..3e4c6c6e4 100644 --- a/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php @@ -5,17 +5,17 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class ForeignKeyDoesNotExist extends SchemaException +final class ForeignKeyDoesNotExist extends LogicException implements SchemaException { public static function new(string $foreignKeyName, string $table): self { return new self( sprintf('There exists no foreign key with the name "%s" on table "%s".', $foreignKeyName, $table), - self::FOREIGNKEY_DOESNT_EXIST, ); } } diff --git a/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php index 25facb58a..e8592bee0 100644 --- a/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php +++ b/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php @@ -5,17 +5,17 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class IndexAlreadyExists extends SchemaException +final class IndexAlreadyExists extends LogicException implements SchemaException { public static function new(string $indexName, string $table): self { return new self( sprintf('An index with name "%s" was already defined on table "%s".', $indexName, $table), - self::INDEX_ALREADY_EXISTS, ); } } diff --git a/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php index 9f4024185..ee25fd852 100644 --- a/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class IndexDoesNotExist extends SchemaException +final class IndexDoesNotExist extends LogicException implements SchemaException { public static function new(string $indexName, string $table): self { - return new self( - sprintf('Index "%s" does not exist on table "%s".', $indexName, $table), - self::INDEX_DOESNT_EXIST, - ); + return new self(sprintf('Index "%s" does not exist on table "%s".', $indexName, $table)); } } diff --git a/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php b/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php index 5230f506e..d295eb8a2 100644 --- a/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php +++ b/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use InvalidArgumentException; use function sprintf; /** @psalm-immutable */ -final class IndexNameInvalid extends SchemaException +final class IndexNameInvalid extends InvalidArgumentException implements SchemaException { public static function new(string $indexName): self { - return new self( - sprintf('Invalid index name "%s" given, has to be [a-zA-Z0-9_].', $indexName), - self::INDEX_INVALID_NAME, - ); + return new self(sprintf('Invalid index name "%s" given, has to be [a-zA-Z0-9_].', $indexName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/InvalidTableName.php b/doctrine/dbal/src/Schema/Exception/InvalidTableName.php index 50e2d3af9..3b5d89dc1 100644 --- a/doctrine/dbal/src/Schema/Exception/InvalidTableName.php +++ b/doctrine/dbal/src/Schema/Exception/InvalidTableName.php @@ -5,11 +5,12 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use InvalidArgumentException; use function sprintf; /** @psalm-immutable */ -final class InvalidTableName extends SchemaException +final class InvalidTableName extends InvalidArgumentException implements SchemaException { public static function new(string $tableName): self { diff --git a/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php b/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php deleted file mode 100644 index 92d237e7a..000000000 --- a/doctrine/dbal/src/Schema/Exception/NamedForeignKeyRequired.php +++ /dev/null @@ -1,30 +0,0 @@ -getName(), - implode(', ', $foreignKey->getColumns()), - $foreignKey->getForeignTableName(), - implode(', ', $foreignKey->getForeignColumns()), - ), - ); - } -} diff --git a/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php index 008bd5f0e..4109af508 100644 --- a/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php +++ b/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class NamespaceAlreadyExists extends SchemaException +final class NamespaceAlreadyExists extends LogicException implements SchemaException { public static function new(string $namespaceName): self { - return new self( - sprintf('The namespace with name "%s" already exists.', $namespaceName), - self::NAMESPACE_ALREADY_EXISTS, - ); + return new self(sprintf('The namespace with name "%s" already exists.', $namespaceName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php index d719c6a06..d374f2737 100644 --- a/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php +++ b/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class SequenceAlreadyExists extends SchemaException +final class SequenceAlreadyExists extends LogicException implements SchemaException { public static function new(string $sequenceName): self { - return new self( - sprintf('The sequence "%s" already exists.', $sequenceName), - self::SEQUENCE_ALREADY_EXISTS, - ); + return new self(sprintf('The sequence "%s" already exists.', $sequenceName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php index 3f948835c..fa98cee02 100644 --- a/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class SequenceDoesNotExist extends SchemaException +final class SequenceDoesNotExist extends LogicException implements SchemaException { public static function new(string $sequenceName): self { - return new self( - sprintf('There exists no sequence with the name "%s".', $sequenceName), - self::SEQUENCE_DOENST_EXIST, - ); + return new self(sprintf('There exists no sequence with the name "%s".', $sequenceName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php b/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php index d7b235937..b978dbc6e 100644 --- a/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php +++ b/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class TableAlreadyExists extends SchemaException +final class TableAlreadyExists extends LogicException implements SchemaException { public static function new(string $tableName): self { - return new self( - sprintf('The table with name "%s" already exists.', $tableName), - self::TABLE_ALREADY_EXISTS, - ); + return new self(sprintf('The table with name "%s" already exists.', $tableName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php index 7c6dda9ab..8c66a11a4 100644 --- a/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php @@ -5,17 +5,15 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class TableDoesNotExist extends SchemaException +final class TableDoesNotExist extends LogicException implements SchemaException { public static function new(string $tableName): self { - return new self( - sprintf('There is no table with name "%s" in the schema.', $tableName), - self::TABLE_DOESNT_EXIST, - ); + return new self(sprintf('There is no table with name "%s" in the schema.', $tableName)); } } diff --git a/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php b/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php index dad6116c6..3ae5aaa27 100644 --- a/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php +++ b/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php @@ -5,17 +5,17 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use LogicException; use function sprintf; /** @psalm-immutable */ -final class UniqueConstraintDoesNotExist extends SchemaException +final class UniqueConstraintDoesNotExist extends LogicException implements SchemaException { public static function new(string $constraintName, string $table): self { return new self( sprintf('There exists no unique constraint with the name "%s" on table "%s".', $constraintName, $table), - self::CONSTRAINT_DOESNT_EXIST, ); } } diff --git a/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php b/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php index 7a7c6f87a..a97ee76e5 100644 --- a/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php +++ b/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php @@ -5,11 +5,12 @@ namespace Doctrine\DBAL\Schema\Exception; use Doctrine\DBAL\Schema\SchemaException; +use InvalidArgumentException; use function sprintf; /** @psalm-immutable */ -final class UnknownColumnOption extends SchemaException +final class UnknownColumnOption extends InvalidArgumentException implements SchemaException { public static function new(string $name): self { diff --git a/doctrine/dbal/src/Schema/ForeignKeyConstraint.php b/doctrine/dbal/src/Schema/ForeignKeyConstraint.php index 2f0311cd6..bb5ef7f04 100644 --- a/doctrine/dbal/src/Schema/ForeignKeyConstraint.php +++ b/doctrine/dbal/src/Schema/ForeignKeyConstraint.php @@ -1,5 +1,7 @@ Identifier) * - * @var Identifier[] + * @var array */ - protected $_localColumnNames; + protected array $_localColumnNames; /** * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. - * - * @var Table|Identifier */ - protected $_foreignTableName; + protected Identifier $_foreignTableName; /** * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. - * array($columnName => Identifier) - * - * @var Identifier[] - */ - protected $_foreignColumnNames; - - /** - * Options associated with the foreign key constraint. * - * @var mixed[] + * @var array */ - protected $_options; + protected array $_foreignColumnNames; /** * Initializes the foreign key constraint. * - * @param string[] $localColumnNames Names of the referencing table columns. - * @param Table|string $foreignTableName Referenced table. - * @param string[] $foreignColumnNames Names of the referenced table columns. - * @param string|null $name Name of the foreign key constraint. - * @param mixed[] $options Options associated with the foreign key constraint. + * @param array $localColumnNames Names of the referencing table columns. + * @param string $foreignTableName Referenced table. + * @param array $foreignColumnNames Names of the referenced table columns. + * @param string $name Name of the foreign key constraint. + * @param array $options Options associated with the foreign key constraint. */ public function __construct( array $localColumnNames, - $foreignTableName, + string $foreignTableName, array $foreignColumnNames, - $name = null, - array $options = [] + string $name = '', + protected array $options = [], ) { - if ($name !== null) { - $this->_setName($name); - } + $this->_setName($name); $this->_localColumnNames = $this->createIdentifierMap($localColumnNames); - - if ($foreignTableName instanceof Table) { - $this->_foreignTableName = $foreignTableName; - } else { - $this->_foreignTableName = new Identifier($foreignTableName); - } + $this->_foreignTableName = new Identifier($foreignTableName); $this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames); - $this->_options = $options; } /** - * @param string[] $names + * @param array $names * - * @return Identifier[] + * @return array */ private function createIdentifierMap(array $names): array { @@ -101,51 +77,13 @@ private function createIdentifierMap(array $names): array return $identifiers; } - /** - * Returns the name of the referencing table - * the foreign key constraint is associated with. - * - * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. - * - * @return string - */ - public function getLocalTableName() - { - return $this->_localTable->getName(); - } - - /** - * Sets the Table instance of the referencing table - * the foreign key constraint is associated with. - * - * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. - * - * @param Table $table Instance of the referencing table. - * - * @return void - */ - public function setLocalTable(Table $table) - { - $this->_localTable = $table; - } - - /** - * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. - * - * @return Table - */ - public function getLocalTable() - { - return $this->_localTable; - } - /** * Returns the names of the referencing table columns * the foreign key constraint is associated with. * - * @return string[] + * @return array */ - public function getLocalColumns() + public function getLocalColumns(): array { return array_keys($this->_localColumnNames); } @@ -160,9 +98,9 @@ public function getLocalColumns() * * @param AbstractPlatform $platform The platform to use for quotation. * - * @return string[] + * @return array */ - public function getQuotedLocalColumns(AbstractPlatform $platform) + public function getQuotedLocalColumns(AbstractPlatform $platform): array { $columns = []; @@ -176,73 +114,36 @@ public function getQuotedLocalColumns(AbstractPlatform $platform) /** * Returns unquoted representation of local table column names for comparison with other FK * - * @return string[] + * @return array */ - public function getUnquotedLocalColumns() + public function getUnquotedLocalColumns(): array { - return array_map([$this, 'trimQuotes'], $this->getLocalColumns()); + return array_map($this->trimQuotes(...), $this->getLocalColumns()); } /** * Returns unquoted representation of foreign table column names for comparison with other FK * - * @return string[] - */ - public function getUnquotedForeignColumns() - { - return array_map([$this, 'trimQuotes'], $this->getForeignColumns()); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see getLocalColumns()} instead. - * - * @see getLocalColumns - */ - public function getColumns() - { - return $this->getLocalColumns(); - } - - /** - * Returns the quoted representation of the referencing table column names - * the foreign key constraint is associated with. - * - * But only if they were defined with one or the referencing table column name - * is a keyword reserved by the platform. - * Otherwise the plain unquoted value as inserted is returned. - * - * @deprecated Use {@see getQuotedLocalColumns()} instead. - * - * @see getQuotedLocalColumns - * - * @param AbstractPlatform $platform The platform to use for quotation. - * - * @return string[] + * @return array */ - public function getQuotedColumns(AbstractPlatform $platform) + public function getUnquotedForeignColumns(): array { - return $this->getQuotedLocalColumns($platform); + return array_map($this->trimQuotes(...), $this->getForeignColumns()); } /** * Returns the name of the referenced table * the foreign key constraint is associated with. - * - * @return string */ - public function getForeignTableName() + public function getForeignTableName(): string { return $this->_foreignTableName->getName(); } /** * Returns the non-schema qualified foreign table name. - * - * @return string */ - public function getUnqualifiedForeignTableName() + public function getUnqualifiedForeignTableName(): string { $name = $this->_foreignTableName->getName(); $position = strrpos($name, '.'); @@ -263,10 +164,8 @@ public function getUnqualifiedForeignTableName() * Otherwise the plain unquoted value as inserted is returned. * * @param AbstractPlatform $platform The platform to use for quotation. - * - * @return string */ - public function getQuotedForeignTableName(AbstractPlatform $platform) + public function getQuotedForeignTableName(AbstractPlatform $platform): string { return $this->_foreignTableName->getQuotedName($platform); } @@ -275,9 +174,9 @@ public function getQuotedForeignTableName(AbstractPlatform $platform) * Returns the names of the referenced table columns * the foreign key constraint is associated with. * - * @return string[] + * @return array */ - public function getForeignColumns() + public function getForeignColumns(): array { return array_keys($this->_foreignColumnNames); } @@ -292,9 +191,9 @@ public function getForeignColumns() * * @param AbstractPlatform $platform The platform to use for quotation. * - * @return string[] + * @return array */ - public function getQuotedForeignColumns(AbstractPlatform $platform) + public function getQuotedForeignColumns(AbstractPlatform $platform): array { $columns = []; @@ -308,45 +207,35 @@ public function getQuotedForeignColumns(AbstractPlatform $platform) /** * Returns whether or not a given option * is associated with the foreign key constraint. - * - * @param string $name Name of the option to check. - * - * @return bool */ - public function hasOption($name) + public function hasOption(string $name): bool { - return isset($this->_options[$name]); + return isset($this->options[$name]); } /** * Returns an option associated with the foreign key constraint. - * - * @param string $name Name of the option the foreign key constraint is associated with. - * - * @return mixed */ - public function getOption($name) + public function getOption(string $name): mixed { - return $this->_options[$name]; + return $this->options[$name]; } /** * Returns the options associated with the foreign key constraint. * - * @return mixed[] + * @return array */ - public function getOptions() + public function getOptions(): array { - return $this->_options; + return $this->options; } /** * Returns the referential action for UPDATE operations * on the referenced table the foreign key constraint is associated with. - * - * @return string|null */ - public function onUpdate() + public function onUpdate(): ?string { return $this->onEvent('onUpdate'); } @@ -354,10 +243,8 @@ public function onUpdate() /** * Returns the referential action for DELETE operations * on the referenced table the foreign key constraint is associated with. - * - * @return string|null */ - public function onDelete() + public function onDelete(): ?string { return $this->onEvent('onDelete'); } @@ -368,10 +255,10 @@ public function onDelete() * * @param string $event Name of the database operation/event to return the referential action for. */ - private function onEvent($event): ?string + private function onEvent(string $event): ?string { - if (isset($this->_options[$event])) { - $onEvent = strtoupper($this->_options[$event]); + if (isset($this->options[$event])) { + $onEvent = strtoupper($this->options[$event]); if ($onEvent !== 'NO ACTION' && $onEvent !== 'RESTRICT') { return $onEvent; @@ -388,10 +275,8 @@ private function onEvent($event): ?string * matches one of the given index's columns, `false` otherwise. * * @param Index $index The index to be checked against. - * - * @return bool */ - public function intersectsIndexColumns(Index $index) + public function intersectsIndexColumns(Index $index): bool { foreach ($index->getColumns() as $indexColumn) { foreach ($this->_localColumnNames as $localColumn) { diff --git a/doctrine/dbal/src/Schema/Identifier.php b/doctrine/dbal/src/Schema/Identifier.php index f34465e9e..c3c84a7b6 100644 --- a/doctrine/dbal/src/Schema/Identifier.php +++ b/doctrine/dbal/src/Schema/Identifier.php @@ -1,5 +1,7 @@ _setName($identifier); diff --git a/doctrine/dbal/src/Schema/Index.php b/doctrine/dbal/src/Schema/Index.php index 84fac4146..175565483 100644 --- a/doctrine/dbal/src/Schema/Index.php +++ b/doctrine/dbal/src/Schema/Index.php @@ -1,9 +1,10 @@ Identifier) * - * @var Identifier[] + * @var array */ - protected $_columns = []; + protected array $_columns = []; - /** @var bool */ - protected $_isUnique = false; + protected bool $_isUnique = false; - /** @var bool */ - protected $_isPrimary = false; + protected bool $_isPrimary = false; /** * Platform specific flags for indexes. - * array($flagName => true) - * - * @var true[] - */ - protected $_flags = []; - - /** - * Platform specific options * - * @todo $_flags should eventually be refactored into options - * @var mixed[] + * @var array */ - private array $options = []; + protected array $_flags = []; /** - * @param string $name - * @param string[] $columns - * @param bool $isUnique - * @param bool $isPrimary - * @param string[] $flags - * @param mixed[] $options + * @param array $columns + * @param array $flags + * @param array $options */ public function __construct( - $name, + ?string $name, array $columns, - $isUnique = false, - $isPrimary = false, + bool $isUnique = false, + bool $isPrimary = false, array $flags = [], - array $options = [] + private readonly array $options = [], ) { $isUnique = $isUnique || $isPrimary; - $this->_setName($name); + if ($name !== null) { + $this->_setName($name); + } + $this->_isUnique = $isUnique; $this->_isPrimary = $isPrimary; - $this->options = $options; foreach ($columns as $column) { $this->_addColumn($column); @@ -77,24 +65,33 @@ public function __construct( } } - /** @throws InvalidArgumentException */ protected function _addColumn(string $column): void { $this->_columns[$column] = new Identifier($column); } /** - * {@inheritDoc} + * Returns the names of the referencing table columns the constraint is associated with. + * + * @return list */ - public function getColumns() + public function getColumns(): array { return array_keys($this->_columns); } /** - * {@inheritDoc} + * Returns the quoted representation of the column names the constraint is associated with. + * + * But only if they were defined with one or a column name + * is a keyword reserved by the platform. + * Otherwise, the plain unquoted value as inserted is returned. + * + * @param AbstractPlatform $platform The platform to use for quotation. + * + * @return list */ - public function getQuotedColumns(AbstractPlatform $platform) + public function getQuotedColumns(AbstractPlatform $platform): array { $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths') ? $this->getOption('lengths') : []; @@ -116,41 +113,31 @@ public function getQuotedColumns(AbstractPlatform $platform) return $columns; } - /** @return string[] */ - public function getUnquotedColumns() + /** @return array */ + public function getUnquotedColumns(): array { - return array_map([$this, 'trimQuotes'], $this->getColumns()); + return array_map($this->trimQuotes(...), $this->getColumns()); } /** * Is the index neither unique nor primary key? - * - * @return bool */ - public function isSimpleIndex() + public function isSimpleIndex(): bool { return ! $this->_isPrimary && ! $this->_isUnique; } - /** @return bool */ - public function isUnique() + public function isUnique(): bool { return $this->_isUnique; } - /** @return bool */ - public function isPrimary() + public function isPrimary(): bool { return $this->_isPrimary; } - /** - * @param string $name - * @param int $pos - * - * @return bool - */ - public function hasColumnAtPosition($name, $pos = 0) + public function hasColumnAtPosition(string $name, int $pos = 0): bool { $name = $this->trimQuotes(strtolower($name)); $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); @@ -161,11 +148,9 @@ public function hasColumnAtPosition($name, $pos = 0) /** * Checks if this index exactly spans the given column names in the correct order. * - * @param string[] $columnNames - * - * @return bool + * @param array $columnNames */ - public function spansColumns(array $columnNames) + public function spansColumns(array $columnNames): bool { $columns = $this->getColumns(); $numberOfColumns = count($columns); @@ -185,18 +170,6 @@ public function spansColumns(array $columnNames) return $sameColumns; } - /** - * Keeping misspelled function name for backwards compatibility - * - * @deprecated Use {@see isFulfilledBy()} instead. - * - * @return bool - */ - public function isFullfilledBy(Index $other) - { - return $this->isFulfilledBy($other); - } - /** * Checks if the other index already fulfills all the indexing and constraint needs of the current one. */ @@ -240,10 +213,8 @@ public function isFulfilledBy(Index $other): bool /** * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. - * - * @return bool */ - public function overrules(Index $other) + public function overrules(Index $other): bool { if ($other->isPrimary()) { return false; @@ -261,9 +232,9 @@ public function overrules(Index $other) /** * Returns platform specific flags for indexes. * - * @return string[] + * @return array */ - public function getFlags() + public function getFlags(): array { return array_keys($this->_flags); } @@ -271,13 +242,9 @@ public function getFlags() /** * Adds Flag for an index that translates to platform specific handling. * - * @param string $flag - * - * @return Index - * * @example $index->addFlag('CLUSTERED') */ - public function addFlag($flag) + public function addFlag(string $flag): self { $this->_flags[strtolower($flag)] = true; @@ -286,50 +253,32 @@ public function addFlag($flag) /** * Does this index have a specific flag? - * - * @param string $flag - * - * @return bool */ - public function hasFlag($flag) + public function hasFlag(string $flag): bool { return isset($this->_flags[strtolower($flag)]); } /** * Removes a flag. - * - * @param string $flag - * - * @return void */ - public function removeFlag($flag) + public function removeFlag(string $flag): void { unset($this->_flags[strtolower($flag)]); } - /** - * @param string $name - * - * @return bool - */ - public function hasOption($name) + public function hasOption(string $name): bool { return isset($this->options[strtolower($name)]); } - /** - * @param string $name - * - * @return mixed - */ - public function getOption($name) + public function getOption(string $name): mixed { return $this->options[strtolower($name)]; } - /** @return mixed[] */ - public function getOptions() + /** @return array */ + public function getOptions(): array { return $this->options; } diff --git a/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php b/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php deleted file mode 100644 index 01c856b6e..000000000 --- a/doctrine/dbal/src/Schema/LegacySchemaManagerFactory.php +++ /dev/null @@ -1,19 +0,0 @@ -getDriver()->getSchemaManager( - $connection, - $connection->getDatabasePlatform(), - ); - } -} diff --git a/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/doctrine/dbal/src/Schema/MySQLSchemaManager.php index 3c00d00e2..249be132e 100644 --- a/doctrine/dbal/src/Schema/MySQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/MySQLSchemaManager.php @@ -1,24 +1,28 @@ "'", ]; - /** - * {@inheritDoc} - */ - public function listTableNames() - { - return $this->doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } + private ?DefaultTableOptions $defaultTableOptions = null; /** * {@inheritDoc} */ - public function listTableForeignKeys($table, $database = null) + protected function _getPortableTableDefinition(array $table): string { - return $this->doListTableForeignKeys($table, $database); + return $table['TABLE_NAME']; } /** * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); } @@ -118,15 +75,7 @@ protected function _getPortableViewDefinition($view) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) - { - return array_shift($table); - } - - /** - * {@inheritDoc} - */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { foreach ($tableIndexes as $k => $v) { $v = array_change_key_case($v, CASE_LOWER); @@ -136,14 +85,14 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null $v['primary'] = false; } - if (strpos($v['index_type'], 'FULLTEXT') !== false) { + if (str_contains($v['index_type'], 'FULLTEXT')) { $v['flags'] = ['FULLTEXT']; - } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { + } elseif (str_contains($v['index_type'], 'SPATIAL')) { $v['flags'] = ['SPATIAL']; } // Ignore prohibited prefix `length` for spatial index - if (strpos($v['index_type'], 'SPATIAL') === false) { + if (! str_contains($v['index_type'], 'SPATIAL')) { $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; } @@ -156,7 +105,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null /** * {@inheritDoc} */ - protected function _getPortableDatabaseDefinition($database) + protected function _getPortableDatabaseDefinition(array $database): string { return $database['Database']; } @@ -164,7 +113,7 @@ protected function _getPortableDatabaseDefinition($database) /** * {@inheritDoc} */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); @@ -174,22 +123,16 @@ protected function _getPortableTableColumnDefinition($tableColumn) $length = $tableColumn['length'] ?? strtok('(), '); - $fixed = null; + $fixed = false; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } - $scale = null; + $scale = 0; $precision = null; - $type = $origType = $this->_platform->getDoctrineTypeMapping($dbType); - - // In cases where not connected to a database DESCRIBE $table does not return 'Comment' - if (isset($tableColumn['comment'])) { - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); - } + $type = $this->platform->getDoctrineTypeMapping($dbType); switch ($dbType) { case 'char': @@ -209,8 +152,8 @@ protected function _getPortableTableColumnDefinition($tableColumn) $match, ) === 1 ) { - $precision = $match[1]; - $scale = $match[2]; + $precision = (int) $match[1]; + $scale = (int) $match[2]; $length = null; } @@ -251,29 +194,25 @@ protected function _getPortableTableColumnDefinition($tableColumn) break; } - if ($this->_platform instanceof MariaDb1027Platform) { - $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); + if ($this->platform instanceof MariaDBPlatform) { + $columnDefault = $this->getMariaDBColumnDefault($this->platform, $tableColumn['default']); } else { $columnDefault = $tableColumn['default']; } $options = [ 'length' => $length !== null ? (int) $length : null, - 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, - 'fixed' => (bool) $fixed, + 'unsigned' => str_contains($tableColumn['type'], 'unsigned'), + 'fixed' => $fixed, 'default' => $columnDefault, 'notnull' => $tableColumn['null'] !== 'YES', - 'scale' => null, - 'precision' => null, - 'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false, - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, + 'scale' => $scale, + 'precision' => $precision, + 'autoincrement' => str_contains($tableColumn['extra'], 'auto_increment'), ]; - if ($scale !== null && $precision !== null) { - $options['scale'] = (int) $scale; - $options['precision'] = (int) $precision; + if (isset($tableColumn['comment'])) { + $options['comment'] = $tableColumn['comment']; } $column = new Column($tableColumn['field'], Type::getType($type), $options); @@ -286,41 +225,9 @@ protected function _getPortableTableColumnDefinition($tableColumn) $column->setPlatformOption('collation', $tableColumn['collation']); } - if (isset($tableColumn['declarationMismatch'])) { - $column->setPlatformOption('declarationMismatch', $tableColumn['declarationMismatch']); - } - - // Check underlying database type where doctrine type is inferred from DC2Type comment - // and set a flag if it is not as expected. - if ($type === 'json' && $origType !== $type && $this->expectedDbType($type, $options) !== $dbType) { - $column->setPlatformOption('declarationMismatch', true); - } - return $column; } - /** - * Returns the database data type for a given doctrine type and column - * - * Note that for data types that depend on length where length is not part of the column definition - * and therefore the $tableColumn['length'] will not be set, for example TEXT (which could be LONGTEXT, - * MEDIUMTEXT) or BLOB (LONGBLOB or TINYBLOB), the expectedDbType cannot be inferred exactly, merely - * the default type. - * - * This method is intended to be used to determine underlying database type where doctrine type is - * inferred from a DC2Type comment. - * - * @param mixed[] $tableColumn - */ - private function expectedDbType(string $type, array $tableColumn): string - { - $_type = Type::getType($type); - $expectedDbType = strtolower($_type->getSQLDeclaration($tableColumn, $this->_platform)); - $expectedDbType = strtok($expectedDbType, '(), '); - - return $expectedDbType === false ? '' : $expectedDbType; - } - /** * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. * @@ -337,7 +244,7 @@ private function expectedDbType(string $type, array $tableColumn): string * * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ - private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string + private function getMariaDBColumnDefault(MariaDBPlatform $platform, ?string $columnDefault): ?string { if ($columnDefault === 'NULL' || $columnDefault === null) { return null; @@ -347,24 +254,18 @@ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?str return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); } - switch ($columnDefault) { - case 'current_timestamp()': - return $platform->getCurrentTimestampSQL(); - - case 'curdate()': - return $platform->getCurrentDateSQL(); - - case 'curtime()': - return $platform->getCurrentTimeSQL(); - } - - return $columnDefault; + return match ($columnDefault) { + 'current_timestamp()' => $platform->getCurrentTimestampSQL(), + 'curdate()' => $platform->getCurrentDateSQL(), + 'curtime()' => $platform->getCurrentTimeSQL(), + default => $columnDefault, + }; } /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $list = []; foreach ($tableForeignKeys as $value) { @@ -398,7 +299,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local'], @@ -412,13 +313,18 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): Fore ); } + /** @throws Exception */ public function createComparator(): Comparator { return new MySQL\Comparator( - $this->_platform, + $this->platform, + new CachingCharsetMetadataProvider( + new ConnectionCharsetMetadataProvider($this->connection), + ), new CachingCollationMetadataProvider( - new ConnectionCollationMetadataProvider($this->_conn), + new ConnectionCollationMetadataProvider($this->connection), ), + $this->getDefaultTableOptions(), ); } @@ -432,13 +338,12 @@ protected function selectTableNames(string $databaseName): Result ORDER BY TABLE_NAME SQL; - return $this->_conn->executeQuery($sql, [$databaseName]); + return $this->connection->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { - // @todo 4.0 - call getColumnTypeSQLSnippet() instead - [$columnTypeSQL, $joinCheckConstraintSQL] = $this->_platform->getColumnTypeSQLSnippets('c', $databaseName); + $columnTypeSQL = $this->platform->getColumnTypeSQLSnippet('c', $databaseName); $sql = 'SELECT'; @@ -459,7 +364,6 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = FROM information_schema.COLUMNS c INNER JOIN information_schema.TABLES t ON t.TABLE_NAME = c.TABLE_NAME - $joinCheckConstraintSQL SQL; // The schema name is passed multiple times as a literal in the WHERE clause instead of using a JOIN condition @@ -475,7 +379,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY ORDINAL_POSITION'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -505,7 +409,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -521,13 +425,13 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, k.REFERENCED_COLUMN_NAME, - k.ORDINAL_POSITION /*!50116, + k.ORDINAL_POSITION, c.UPDATE_RULE, - c.DELETE_RULE */ -FROM information_schema.key_column_usage k /*!50116 + c.DELETE_RULE +FROM information_schema.key_column_usage k INNER JOIN information_schema.referential_constraints c ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME -AND c.TABLE_NAME = k.TABLE_NAME */ +AND c.TABLE_NAME = k.TABLE_NAME SQL; $conditions = ['k.TABLE_SCHEMA = ?']; @@ -538,18 +442,17 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $params[] = $tableName; } + // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition + // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions + // caused by https://bugs.mysql.com/bug.php?id=81347 + $conditions[] = 'c.CONSTRAINT_SCHEMA = ?'; + $params[] = $databaseName; + $conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL'; - $sql .= ' WHERE ' . implode(' AND ', $conditions) - // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition - // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions - // caused by https://bugs.mysql.com/bug.php?id=81347. - // Use a string literal for the database name since the internal PDO SQL parser - // cannot recognize parameter placeholders inside conditional comments - . ' /*!50116 AND c.CONSTRAINT_SCHEMA = ' . $this->_conn->quote($databaseName) . ' */' - . ' ORDER BY k.ORDINAL_POSITION'; + $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY k.ORDINAL_POSITION'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } /** @@ -557,6 +460,11 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { + // MariaDB-10.10.1 added FULL_COLLATION_NAME to the information_schema.COLLATION_CHARACTER_SET_APPLICABILITY. + // A base collation like uca1400_ai_ci can refer to multiple character sets. The value in the + // information_schema.TABLES.TABLE_COLLATION corresponds to the full collation name. + // The MariaDB executable comment syntax with version, /*M!101001, is exclusively executed on + // MariaDB-10.10.1+ servers for backwards compatibility, and compatiblity to MySQL servers. $sql = <<<'SQL' SELECT t.TABLE_NAME, t.ENGINE, @@ -567,7 +475,8 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table ccsa.CHARACTER_SET_NAME FROM information_schema.TABLES t INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa - ON ccsa.COLLATION_NAME = t.TABLE_COLLATION + ON /*M!101001 ccsa.FULL_COLLATION_NAME = t.TABLE_COLLATION OR */ + ccsa.COLLATION_NAME = t.TABLE_COLLATION SQL; $conditions = ['t.TABLE_SCHEMA = ?']; @@ -583,7 +492,7 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table $sql .= ' WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ - $metadata = $this->_conn->executeQuery($sql, $params) + $metadata = $this->connection->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; @@ -603,7 +512,7 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table return $tableOptions; } - /** @return string[]|true[] */ + /** @return array|array */ private function parseCreateOptions(?string $string): array { $options = []; @@ -620,4 +529,20 @@ private function parseCreateOptions(?string $string): array return $options; } + + /** @throws Exception */ + private function getDefaultTableOptions(): DefaultTableOptions + { + if ($this->defaultTableOptions === null) { + $row = $this->connection->fetchNumeric( + 'SELECT @@character_set_database, @@collation_database', + ); + + assert($row !== false); + + $this->defaultTableOptions = new DefaultTableOptions(...$row); + } + + return $this->defaultTableOptions; + } } diff --git a/doctrine/dbal/src/Schema/OracleSchemaManager.php b/doctrine/dbal/src/Schema/OracleSchemaManager.php index 073752214..f973eaa3e 100644 --- a/doctrine/dbal/src/Schema/OracleSchemaManager.php +++ b/doctrine/dbal/src/Schema/OracleSchemaManager.php @@ -1,20 +1,25 @@ doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } - - /** - * {@inheritDoc} - */ - public function listTableForeignKeys($table, $database = null) - { - return $this->doListTableForeignKeys($table, $database); - } - - /** - * {@inheritDoc} - */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { $view = array_change_key_case($view, CASE_LOWER); @@ -98,7 +46,7 @@ protected function _getPortableViewDefinition($view) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableTableDefinition(array $table): string { $table = array_change_key_case($table, CASE_LOWER); @@ -110,7 +58,7 @@ protected function _getPortableTableDefinition($table) * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { $indexBuffer = []; foreach ($tableIndexes as $tableIndex) { @@ -139,25 +87,29 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null /** * {@inheritDoc} */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); $dbType = strtolower($tableColumn['data_type']); - if (strpos($dbType, 'timestamp(') === 0) { - if (strpos($dbType, 'with time zone') !== false) { + if (str_starts_with($dbType, 'timestamp(')) { + if (str_contains($dbType, 'with time zone')) { $dbType = 'timestamptz'; } else { $dbType = 'timestamp'; } } - $unsigned = $fixed = $precision = $scale = $length = null; + $length = $precision = null; + $scale = 0; + $fixed = false; if (! isset($tableColumn['column_name'])) { $tableColumn['column_name'] = ''; } + assert(array_key_exists('data_default', $tableColumn)); + // Default values returned from database sometimes have trailing spaces. if (is_string($tableColumn['data_default'])) { $tableColumn['data_default'] = trim($tableColumn['data_default']); @@ -182,9 +134,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) $scale = (int) $tableColumn['data_scale']; } - $type = $this->_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); - $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); + $type = $this->platform->getDoctrineTypeMapping($dbType); switch ($dbType) { case 'number': @@ -203,42 +153,41 @@ protected function _getPortableTableColumnDefinition($tableColumn) case 'varchar': case 'varchar2': case 'nvarchar2': - $length = $tableColumn['char_length']; - $fixed = false; + $length = (int) $tableColumn['char_length']; break; case 'raw': - $length = $tableColumn['data_length']; + $length = (int) $tableColumn['data_length']; $fixed = true; break; case 'char': case 'nchar': - $length = $tableColumn['char_length']; + $length = (int) $tableColumn['char_length']; $fixed = true; break; } $options = [ 'notnull' => $tableColumn['nullable'] === 'N', - 'fixed' => (bool) $fixed, - 'unsigned' => (bool) $unsigned, + 'fixed' => $fixed, 'default' => $tableColumn['data_default'], 'length' => $length, 'precision' => $precision, 'scale' => $scale, - 'comment' => isset($tableColumn['comments']) && $tableColumn['comments'] !== '' - ? $tableColumn['comments'] - : null, ]; + if (isset($tableColumn['comments'])) { + $options['comment'] = $tableColumn['comments']; + } + return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); } /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $list = []; foreach ($tableForeignKeys as $value) { @@ -270,7 +219,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( array_values($tableForeignKey['local']), @@ -284,7 +233,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): Fore /** * {@inheritDoc} */ - protected function _getPortableSequenceDefinition($sequence) + protected function _getPortableSequenceDefinition(array $sequence): Sequence { $sequence = array_change_key_case($sequence, CASE_LOWER); @@ -298,57 +247,46 @@ protected function _getPortableSequenceDefinition($sequence) /** * {@inheritDoc} */ - protected function _getPortableDatabaseDefinition($database) + protected function _getPortableDatabaseDefinition(array $database): string { $database = array_change_key_case($database, CASE_LOWER); return $database['username']; } - /** - * {@inheritDoc} - */ - public function createDatabase($database) + public function createDatabase(string $database): void { - $statement = $this->_platform->getCreateDatabaseSQL($database); + $statement = $this->platform->getCreateDatabaseSQL($database); - $params = $this->_conn->getParams(); + $params = $this->connection->getParams(); if (isset($params['password'])) { $statement .= ' IDENTIFIED BY ' . $params['password']; } - $this->_conn->executeStatement($statement); + $this->connection->executeStatement($statement); $statement = 'GRANT DBA TO ' . $database; - $this->_conn->executeStatement($statement); + $this->connection->executeStatement($statement); } - /** - * @internal The method should be only used from within the OracleSchemaManager class hierarchy. - * - * @param string $table - * - * @return bool - * - * @throws Exception - */ - public function dropAutoincrement($table) + /** @throws Exception */ + protected function dropAutoincrement(string $table): bool { - $sql = $this->_platform->getDropAutoincrementSql($table); + $sql = $this->platform->getDropAutoincrementSql($table); foreach ($sql as $query) { - $this->_conn->executeStatement($query); + $this->connection->executeStatement($query); } return true; } - /** - * {@inheritDoc} - */ - public function dropTable($name) + public function dropTable(string $name): void { - $this->tryMethod('dropAutoincrement', $name); + try { + $this->dropAutoincrement($name); + } catch (DatabaseObjectNotFoundException) { + } parent::dropTable($name); } @@ -358,13 +296,11 @@ public function dropTable($name) * * Quotes non-uppercase identifiers explicitly to preserve case * and thus make references to the particular identifier work. - * - * @param string $identifier The identifier to quote. */ - private function getQuotedIdentifierName($identifier): string + private function getQuotedIdentifierName(string $identifier): string { if (preg_match('/[a-z]/', $identifier) === 1) { - return $this->_platform->quoteIdentifier($identifier); + return $this->platform->quoteIdentifier($identifier); } return $identifier; @@ -379,7 +315,7 @@ protected function selectTableNames(string $databaseName): Result ORDER BY TABLE_NAME SQL; - return $this->_conn->executeQuery($sql, ['OWNER' => $databaseName]); + return $this->connection->executeQuery($sql, ['OWNER' => $databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result @@ -420,7 +356,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -458,7 +394,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' . ', IND_COL.COLUMN_POSITION'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -494,7 +430,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' . ', COLS.POSITION'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } /** @@ -515,7 +451,7 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ - $metadata = $this->_conn->executeQuery($sql, $params) + $metadata = $this->connection->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; diff --git a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php index 13fde63b2..9af16c998 100644 --- a/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php +++ b/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php @@ -1,5 +1,7 @@ doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } - - /** - * {@inheritDoc} - */ - public function listTableForeignKeys($table, $database = null) - { - return $this->doListTableForeignKeys($table, $database); - } - - /** - * Gets all the existing schema names. - * - * @deprecated Use {@see listSchemaNames()} instead. - * - * @return string[] - * - * @throws Exception - */ - public function getSchemaNames() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'PostgreSQLSchemaManager::getSchemaNames() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', - ); - - return $this->listNamespaceNames(); - } + private ?string $currentSchema = null; /** * {@inheritDoc} */ public function listSchemaNames(): array { - return $this->_conn->fetchFirstColumn( + return $this->connection->fetchFirstColumn( <<<'SQL' SELECT schema_name FROM information_schema.schemata @@ -133,94 +52,42 @@ public function listSchemaNames(): array ); } - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getSchemaSearchPaths() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4821', - 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.', - ); - - $params = $this->_conn->getParams(); - - $searchPaths = $this->_conn->fetchOne('SHOW search_path'); - assert($searchPaths !== false); - - $schema = explode(',', $searchPaths); - - if (isset($params['user'])) { - $schema = str_replace('"$user"', $params['user'], $schema); - } - - return array_map('trim', $schema); - } - - /** - * Gets names of all existing schemas in the current users search path. - * - * This is a PostgreSQL only function. - * - * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. - * - * @return string[] - * - * @throws Exception - */ - public function getExistingSchemaSearchPaths() + public function createSchemaConfig(): SchemaConfig { - if ($this->existingSchemaPaths === null) { - $this->determineExistingSchemaSearchPaths(); - } + $config = parent::createSchemaConfig(); - assert($this->existingSchemaPaths !== null); + $config->setName($this->getCurrentSchema()); - return $this->existingSchemaPaths; + return $config; } /** * Returns the name of the current schema. * - * @return string|null - * * @throws Exception */ - protected function getCurrentSchema() + protected function getCurrentSchema(): ?string { - $schemas = $this->getExistingSchemaSearchPaths(); - - return array_shift($schemas); + return $this->currentSchema ??= $this->determineCurrentSchema(); } /** - * Sets or resets the order of the existing schemas in the current search path of the user. - * - * This is a PostgreSQL only function. - * - * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. - * - * @return void + * Determines the name of the current schema. * * @throws Exception */ - public function determineExistingSchemaSearchPaths() + protected function determineCurrentSchema(): string { - $names = $this->listSchemaNames(); - $paths = $this->getSchemaSearchPaths(); + $currentSchema = $this->connection->fetchOne('SELECT current_schema()'); + assert(is_string($currentSchema)); - $this->existingSchemaPaths = array_filter($paths, static function ($v) use ($names): bool { - return in_array($v, $names, true); - }); + return $currentSchema; } /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { $onUpdate = null; $onDelete = null; @@ -266,7 +133,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) /** * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); } @@ -274,7 +141,7 @@ protected function _getPortableViewDefinition($view) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableTableDefinition(array $table): string { $currentSchema = $this->getCurrentSchema(); @@ -290,7 +157,7 @@ protected function _getPortableTableDefinition($table) * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { $buffer = []; foreach ($tableIndexes as $row) { @@ -301,7 +168,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null implode(' ,', $colNumbers), ); - $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); + $indexColumns = $this->connection->fetchAllAssociative($columnNameSql); // required for getting the order of the columns right. foreach ($colNumbers as $colNum) { @@ -327,32 +194,15 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null /** * {@inheritDoc} */ - protected function _getPortableDatabaseDefinition($database) + protected function _getPortableDatabaseDefinition(array $database): string { return $database['datname']; } /** * {@inheritDoc} - * - * @deprecated Use {@see listSchemaNames()} instead. */ - protected function getPortableNamespaceDefinition(array $namespace) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', - ); - - return $namespace['nspname']; - } - - /** - * {@inheritDoc} - */ - protected function _getPortableSequenceDefinition($sequence) + protected function _getPortableSequenceDefinition(array $sequence): Sequence { if ($sequence['schemaname'] !== 'public') { $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; @@ -366,29 +216,26 @@ protected function _getPortableSequenceDefinition($sequence) /** * {@inheritDoc} */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); - if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { - // get length from varchar definition - $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); - $tableColumn['length'] = $length; - } - - $matches = []; - - $autoincrement = false; + $length = null; if ( - $tableColumn['default'] !== null - && preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches) === 1 + in_array(strtolower($tableColumn['type']), ['varchar', 'bpchar'], true) + && preg_match('/\((\d*)\)/', $tableColumn['complete_type'], $matches) === 1 ) { - $tableColumn['sequence'] = $matches[1]; - $tableColumn['default'] = null; - $autoincrement = true; + $length = (int) $matches[1]; } + $autoincrement = $tableColumn['attidentity'] === 'd'; + + $matches = []; + + assert(array_key_exists('default', $tableColumn)); + assert(array_key_exists('complete_type', $tableColumn)); + if ($tableColumn['default'] !== null) { if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) { $tableColumn['default'] = $matches[1]; @@ -397,8 +244,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } } - $length = $tableColumn['length'] ?? null; - if ($length === '-1' && isset($tableColumn['atttypmod'])) { + if ($length === -1 && isset($tableColumn['atttypmod'])) { $length = $tableColumn['atttypmod'] - 4; } @@ -406,48 +252,37 @@ protected function _getPortableTableColumnDefinition($tableColumn) $length = null; } - $fixed = null; + $fixed = false; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } $precision = null; - $scale = null; + $scale = 0; $jsonb = null; $dbType = strtolower($tableColumn['type']); if ( $tableColumn['domain_type'] !== null && $tableColumn['domain_type'] !== '' - && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type']) + && ! $this->platform->hasDoctrineTypeMappingFor($tableColumn['type']) ) { $dbType = strtolower($tableColumn['domain_type']); $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; } - $type = $this->_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + $type = $this->platform->getDoctrineTypeMapping($dbType); switch ($dbType) { case 'smallint': case 'int2': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; - break; - case 'int': case 'int4': case 'integer': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; - break; - case 'bigint': case 'int8': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - $length = null; + $length = null; break; case 'bool': @@ -463,14 +298,11 @@ protected function _getPortableTableColumnDefinition($tableColumn) $length = null; break; + case 'json': case 'text': case '_varchar': case 'varchar': $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); - $fixed = false; - break; - case 'interval': - $fixed = false; break; case 'char': @@ -487,8 +319,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) case 'decimal': case 'money': case 'numeric': - $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); - if ( preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', @@ -496,8 +326,8 @@ protected function _getPortableTableColumnDefinition($tableColumn) $match, ) === 1 ) { - $precision = $match[1]; - $scale = $match[2]; + $precision = (int) $match[1]; + $scale = (int) $match[2]; $length = null; } @@ -514,7 +344,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } if ( - $tableColumn['default'] !== null && preg_match( + is_string($tableColumn['default']) && preg_match( "('([^']+)'::)", $tableColumn['default'], $match, @@ -531,54 +361,25 @@ protected function _getPortableTableColumnDefinition($tableColumn) 'scale' => $scale, 'fixed' => $fixed, 'autoincrement' => $autoincrement, - 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' - ? $tableColumn['comment'] - : null, ]; + if (isset($tableColumn['comment'])) { + $options['comment'] = $tableColumn['comment']; + } + $column = new Column($tableColumn['field'], Type::getType($type), $options); if (! empty($tableColumn['collation'])) { $column->setPlatformOption('collation', $tableColumn['collation']); } - if ($column->getType()->getName() === Types::JSON) { - if (! $column->getType() instanceof JsonType) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5049', - <<<'DEPRECATION' - %s not extending %s while being named %s is deprecated, - and will lead to jsonb never to being used in 4.0., - DEPRECATION, - get_class($column->getType()), - JsonType::class, - Types::JSON, - ); - } - + if ($column->getType() instanceof JsonType) { $column->setPlatformOption('jsonb', $jsonb); } return $column; } - /** - * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. - * - * @param mixed $defaultValue - * - * @return mixed - */ - private function fixVersion94NegativeNumericDefaultValue($defaultValue) - { - if ($defaultValue !== null && strpos($defaultValue, '(') === 0) { - return trim($defaultValue, '()'); - } - - return $defaultValue; - } - /** * Parses a default value expression as given by PostgreSQL */ @@ -605,7 +406,7 @@ protected function selectTableNames(string $databaseName): Result AND table_type = 'BASE TABLE' SQL; - return $this->_conn->executeQuery($sql, [$databaseName]); + return $this->connection->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result @@ -626,6 +427,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = (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, a.attnotnull AS isnotnull, + a.attidentity, (SELECT 't' FROM pg_index WHERE c.oid = pg_index.indrelid @@ -661,7 +463,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum'; - return $this->_conn->executeQuery($sql); + return $this->connection->executeQuery($sql); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -695,7 +497,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ')'; - return $this->_conn->executeQuery($sql); + return $this->connection->executeQuery($sql); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -722,7 +524,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= ' WHERE ' . implode(' AND ', $conditions) . ") AND r.contype = 'f'"; - return $this->_conn->executeQuery($sql); + return $this->connection->executeQuery($sql); } /** @@ -743,28 +545,24 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table $sql .= ' WHERE ' . implode(' AND ', $conditions); - return $this->_conn->fetchAllAssociativeIndexed($sql); + return $this->connection->fetchAllAssociativeIndexed($sql); } - /** - * @param string|null $tableName - * - * @return list - */ - private function buildQueryConditions($tableName): array + /** @return list */ + private function buildQueryConditions(?string $tableName): array { $conditions = []; if ($tableName !== null) { - if (strpos($tableName, '.') !== false) { + if (str_contains($tableName, '.')) { [$schemaName, $tableName] = explode('.', $tableName); - $conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName); + $conditions[] = 'n.nspname = ' . $this->platform->quoteStringLiteral($schemaName); } else { $conditions[] = 'n.nspname = ANY(current_schemas(false))'; } $identifier = new Identifier($tableName); - $conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName()); + $conditions[] = 'c.relname = ' . $this->platform->quoteStringLiteral($identifier->getName()); } $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')"; diff --git a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php index acef511ab..e0a74ce2a 100644 --- a/doctrine/dbal/src/Schema/SQLServerSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLServerSchemaManager.php @@ -1,5 +1,7 @@ doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } - - /** - * {@inheritDoc} - */ - public function listTableForeignKeys($table, $database = null) - { - return $this->doListTableForeignKeys($table, $database); - } - /** * {@inheritDoc} */ public function listSchemaNames(): array { - return $this->_conn->fetchFirstColumn( + return $this->connection->fetchFirstColumn( <<<'SQL' SELECT name FROM sys.schemas @@ -106,7 +49,7 @@ public function listSchemaNames(): array /** * {@inheritDoc} */ - protected function _getPortableSequenceDefinition($sequence) + protected function _getPortableSequenceDefinition(array $sequence): Sequence { return new Sequence($sequence['name'], (int) $sequence['increment'], (int) $sequence['start_value']); } @@ -114,21 +57,28 @@ protected function _getPortableSequenceDefinition($sequence) /** * {@inheritDoc} */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { $dbType = strtok($tableColumn['type'], '(), '); assert(is_string($dbType)); - $fixed = null; - $length = (int) $tableColumn['length']; - $default = $tableColumn['default']; + $length = (int) $tableColumn['length']; + + $precision = null; + + $scale = 0; + $fixed = false; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } - if ($default !== null) { - $default = $this->parseDefaultExpression($default); + if ($tableColumn['scale'] !== null) { + $scale = (int) $tableColumn['scale']; + } + + if ($tableColumn['precision'] !== null) { + $precision = (int) $tableColumn['precision']; } switch ($dbType) { @@ -167,27 +117,36 @@ protected function _getPortableTableColumnDefinition($tableColumn) $fixed = true; } - $type = $this->_platform->getDoctrineTypeMapping($dbType); - $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); - $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); + $type = $this->platform->getDoctrineTypeMapping($dbType); $options = [ - 'unsigned' => false, - 'fixed' => (bool) $fixed, - 'default' => $default, + 'fixed' => $fixed, 'notnull' => (bool) $tableColumn['notnull'], - 'scale' => $tableColumn['scale'], - 'precision' => $tableColumn['precision'], + 'scale' => $scale, + 'precision' => $precision, 'autoincrement' => (bool) $tableColumn['autoincrement'], - 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; + if (isset($tableColumn['comment'])) { + $options['comment'] = $tableColumn['comment']; + } + if ($length !== 0 && ($type === 'text' || $type === 'string' || $type === 'binary')) { $options['length'] = $length; } $column = new Column($tableColumn['name'], Type::getType($type), $options); + if ($tableColumn['default'] !== null) { + $default = $this->parseDefaultExpression($tableColumn['default']); + + $column->setDefault($default); + $column->setPlatformOption( + SQLServerPlatform::OPTION_DEFAULT_CONSTRAINT_NAME, + $tableColumn['df_name'], + ); + } + if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { $column->setPlatformOption('collation', $tableColumn['collation']); } @@ -210,7 +169,7 @@ private function parseDefaultExpression(string $value): ?string } if ($value === 'getdate()') { - return $this->_platform->getCurrentTimestampSQL(); + return $this->platform->getCurrentTimestampSQL(); } return $value; @@ -219,7 +178,7 @@ private function parseDefaultExpression(string $value): ?string /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $foreignKeys = []; @@ -249,7 +208,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) /** * {@inheritDoc} */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { foreach ($tableIndexes as &$tableIndex) { $tableIndex['non_unique'] = (bool) $tableIndex['non_unique']; @@ -263,7 +222,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey) + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local_columns'], @@ -277,7 +236,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableTableDefinition(array $table): string { if ($table['schema_name'] !== 'dbo') { return $table['schema_name'] . '.' . $table['table_name']; @@ -289,105 +248,32 @@ protected function _getPortableTableDefinition($table) /** * {@inheritDoc} */ - protected function _getPortableDatabaseDefinition($database) + protected function _getPortableDatabaseDefinition(array $database): string { return $database['name']; } - /** - * {@inheritDoc} - * - * @deprecated Use {@see listSchemaNames()} instead. - */ - protected function getPortableNamespaceDefinition(array $namespace) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4503', - 'SQLServerSchemaManager::getPortableNamespaceDefinition() is deprecated,' - . ' use SQLServerSchemaManager::listSchemaNames() instead.', - ); - - return $namespace['name']; - } - /** * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { - // @todo return new View($view['name'], $view['definition']); } - /** - * {@inheritDoc} - */ - public function alterTable(TableDiff $tableDiff) - { - $droppedColumns = $tableDiff->getDroppedColumns(); - - if (count($droppedColumns) > 0) { - $tableName = ($tableDiff->getOldTable() ?? $tableDiff->getName($this->_platform))->getName(); - - foreach ($droppedColumns as $col) { - foreach ($this->getColumnConstraints($tableName, $col->getName()) as $constraint) { - $this->_conn->executeStatement( - sprintf( - 'ALTER TABLE %s DROP CONSTRAINT %s', - $tableName, - $constraint, - ), - ); - } - } - } - - parent::alterTable($tableDiff); - } - - /** - * Returns the names of the constraints for a given column. - * - * @return iterable - * - * @throws Exception - */ - private function getColumnConstraints(string $table, string $column): iterable - { - return $this->_conn->iterateColumn( - <<<'SQL' -SELECT o.name -FROM sys.objects o - INNER JOIN sys.objects t - ON t.object_id = o.parent_object_id - AND t.type = 'U' - INNER JOIN sys.default_constraints dc - ON dc.object_id = o.object_id - INNER JOIN sys.columns c - ON c.column_id = dc.parent_column_id - AND c.object_id = t.object_id -WHERE t.name = ? - AND c.name = ? -SQL - , - [$table, $column], - ); - } - /** @throws Exception */ public function createComparator(): Comparator { - return new SQLServer\Comparator($this->_platform, $this->getDatabaseCollation()); + return new SQLServer\Comparator($this->platform, $this->getDatabaseCollation()); } /** @throws Exception */ private function getDatabaseCollation(): string { if ($this->databaseCollation === null) { - $databaseCollation = $this->_conn->fetchOne( + $databaseCollation = $this->connection->fetchOne( 'SELECT collation_name FROM sys.databases WHERE name = ' - . $this->_platform->getCurrentDatabaseExpression(), + . $this->platform->getCurrentDatabaseExpression(), ); // a database is always selected, even if omitted in the connection parameters @@ -411,7 +297,7 @@ protected function selectTableNames(string $databaseName): Result ORDER BY name SQL; - return $this->_conn->executeQuery($sql); + return $this->connection->executeQuery($sql); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result @@ -428,6 +314,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = col.max_length AS length, ~col.is_nullable AS notnull, def.definition AS [default], + def.name AS df_name, col.scale, col.precision, col.is_identity AS autoincrement, @@ -460,7 +347,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions); - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -504,7 +391,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' ORDER BY idx.index_id, idxcol.key_ordinal'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -546,7 +433,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= ' ORDER BY fc.constraint_column_id'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } /** @@ -573,7 +460,7 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table $sql .= ' WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ - $metadata = $this->_conn->executeQuery($sql, $params) + $metadata = $this->connection->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; @@ -595,15 +482,15 @@ protected function fetchTableOptionsByTable(string $databaseName, ?string $table * @param string $schemaColumn The name of the column to compare the schema to in the where clause. * @param string $tableColumn The name of the column to compare the table to in the where clause. */ - private function getTableWhereClause($table, $schemaColumn, $tableColumn): string + private function getTableWhereClause(string $table, string $schemaColumn, string $tableColumn): string { - if (strpos($table, '.') !== false) { + if (str_contains($table, '.')) { [$schema, $table] = explode('.', $table); - $schema = $this->_platform->quoteStringLiteral($schema); - $table = $this->_platform->quoteStringLiteral($table); + $schema = $this->platform->quoteStringLiteral($schema); + $table = $this->platform->quoteStringLiteral($table); } else { $schema = 'SCHEMA_NAME()'; - $table = $this->_platform->quoteStringLiteral($table); + $table = $this->platform->quoteStringLiteral($table); } return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); diff --git a/doctrine/dbal/src/Schema/SqliteSchemaManager.php b/doctrine/dbal/src/Schema/SQLiteSchemaManager.php similarity index 66% rename from doctrine/dbal/src/Schema/SqliteSchemaManager.php rename to doctrine/dbal/src/Schema/SQLiteSchemaManager.php index 13f5c5147..c001c2504 100644 --- a/doctrine/dbal/src/Schema/SqliteSchemaManager.php +++ b/doctrine/dbal/src/Schema/SQLiteSchemaManager.php @@ -1,95 +1,45 @@ + * @extends AbstractSchemaManager */ -class SqliteSchemaManager extends AbstractSchemaManager +class SQLiteSchemaManager extends AbstractSchemaManager { - /** - * {@inheritDoc} - */ - public function listTableNames() - { - return $this->doListTableNames(); - } - - /** - * {@inheritDoc} - */ - public function listTables() - { - return $this->doListTables(); - } - - /** - * {@inheritDoc} - * - * @deprecated Use {@see introspectTable()} instead. - */ - public function listTableDetails($name) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5595', - '%s is deprecated. Use introspectTable() instead.', - __METHOD__, - ); - - return $this->doListTableDetails($name); - } - - /** - * {@inheritDoc} - */ - public function listTableColumns($table, $database = null) - { - return $this->doListTableColumns($table, $database); - } - - /** - * {@inheritDoc} - */ - public function listTableIndexes($table) - { - return $this->doListTableIndexes($table); - } - /** * {@inheritDoc} */ @@ -106,103 +56,30 @@ protected function fetchForeignKeyColumnsByTable(string $databaseName): array return $columnsByTable; } - /** - * {@inheritDoc} - * - * @deprecated Delete the database file using the filesystem. - */ - public function dropDatabase($database) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4963', - 'SqliteSchemaManager::dropDatabase() is deprecated. Delete the database file using the filesystem.', - ); - - if (! file_exists($database)) { - return; - } - - unlink($database); - } - - /** - * {@inheritDoc} - * - * @deprecated The engine will create the database file automatically. - */ - public function createDatabase($database) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4963', - 'SqliteSchemaManager::createDatabase() is deprecated.' - . ' The engine will create the database file automatically.', - ); - - $params = $this->_conn->getParams(); - - $params['path'] = $database; - unset($params['memory']); - - $conn = DriverManager::getConnection($params); - $conn->connect(); - $conn->close(); - } - - /** - * {@inheritDoc} - */ - public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) + public function createForeignKey(ForeignKeyConstraint $foreignKey, string $table): void { - if (! $table instanceof Table) { - $table = $this->listTableDetails($table); - } + $table = $this->introspectTable($table); - $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [$foreignKey])); + $this->alterTable(new TableDiff($table, [], [], [], [], [], [], [], [], [$foreignKey], [], [])); } - /** - * {@inheritDoc} - * - * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. - */ - public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) + public function dropForeignKey(string $name, string $table): void { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4897', - 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' - . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.', - ); - - if (! $table instanceof Table) { - $table = $this->listTableDetails($table); - } - - $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [$foreignKey])); - } + $table = $this->introspectTable($table); - /** - * {@inheritDoc} - */ - public function dropForeignKey($foreignKey, $table) - { - if (! $table instanceof Table) { - $table = $this->listTableDetails($table); - } + $foreignKey = $table->getForeignKey($name); - $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [], [$foreignKey])); + $this->alterTable(new TableDiff($table, [], [], [], [], [], [], [], [], [], [], [$foreignKey])); } /** * {@inheritDoc} */ - public function listTableForeignKeys($table, $database = null) + public function listTableForeignKeys(string $table): array { $table = $this->normalizeName($table); - $columns = $this->selectForeignKeyColumns('', $table) + $columns = $this->selectForeignKeyColumns('main', $table) ->fetchAllAssociative(); if (count($columns) > 0) { @@ -215,7 +92,7 @@ public function listTableForeignKeys($table, $database = null) /** * {@inheritDoc} */ - protected function _getPortableTableDefinition($table) + protected function _getPortableTableDefinition(array $table): string { return $table['table_name']; } @@ -225,12 +102,12 @@ protected function _getPortableTableDefinition($table) * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ - protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) + protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array { $indexBuffer = []; // fetch primary - $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]); + $indexArray = $this->connection->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]); usort( $indexArray, @@ -263,7 +140,7 @@ static function (array $a, array $b): int { // fetch regular indexes foreach ($tableIndexes as $tableIndex) { // Ignore indexes with reserved names, e.g. autoindexes - if (strpos($tableIndex['name'], 'sqlite_') === 0) { + if (str_starts_with($tableIndex['name'], 'sqlite_')) { continue; } @@ -273,7 +150,7 @@ static function (array $a, array $b): int { $idx['primary'] = false; $idx['non_unique'] = ! $tableIndex['unique']; - $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]); + $indexArray = $this->connection->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]); foreach ($indexArray as $indexColumnRow) { $idx['column_name'] = $indexColumnRow['name']; @@ -287,7 +164,7 @@ static function (array $a, array $b): int { /** * {@inheritDoc} */ - protected function _getPortableTableColumnList($table, $database, $tableColumns) + protected function _getPortableTableColumnList(string $table, string $database, array $tableColumns): array { $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); @@ -333,18 +210,6 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) $comment = $this->parseColumnCommentFromSQL($columnName, $createSql); - if ($comment === null) { - continue; - } - - $type = $this->extractDoctrineTypeFromComment($comment, ''); - - if ($type !== '') { - $column->setType(Type::getType($type)); - - $comment = $this->removeDoctrineTypeFromComment($comment, $type); - } - $column->setComment($comment); } @@ -354,26 +219,29 @@ protected function _getPortableTableColumnList($table, $database, $tableColumns) /** * {@inheritDoc} */ - protected function _getPortableTableColumnDefinition($tableColumn) + protected function _getPortableTableColumnDefinition(array $tableColumn): Column { - $parts = explode('(', $tableColumn['type']); - $tableColumn['type'] = trim($parts[0]); - if (isset($parts[1])) { - $length = trim($parts[1], ')'); - $tableColumn['length'] = $length; - } + preg_match('/^([^()]*)\\s*(\\(((\\d+)(,\\s*(\\d+))?)\\))?/', $tableColumn['type'], $matches); + + $dbType = trim(strtolower($matches[1])); + + $length = $precision = $unsigned = null; + $fixed = $unsigned = false; + $scale = 0; - $dbType = strtolower($tableColumn['type']); - $length = $tableColumn['length'] ?? null; - $unsigned = false; + if (count($matches) >= 6) { + $precision = (int) $matches[4]; + $scale = (int) $matches[6]; + } elseif (count($matches) >= 4) { + $length = (int) $matches[4]; + } - if (strpos($dbType, ' unsigned') !== false) { + if (str_contains($dbType, ' unsigned')) { $dbType = str_replace(' unsigned', '', $dbType); $unsigned = true; } - $fixed = false; - $type = $this->_platform->getDoctrineTypeMapping($dbType); + $type = $this->platform->getDoctrineTypeMapping($dbType); $default = $tableColumn['dflt_value']; if ($default === 'NULL') { $default = null; @@ -392,28 +260,8 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['name'] = ''; } - $precision = null; - $scale = null; - - switch ($dbType) { - case 'char': - $fixed = true; - break; - case 'float': - case 'double': - case 'real': - case 'decimal': - case 'numeric': - if (isset($tableColumn['length'])) { - if (strpos($tableColumn['length'], ',') === false) { - $tableColumn['length'] .= ',0'; - } - - [$precision, $scale] = array_map('trim', explode(',', $tableColumn['length'])); - } - - $length = null; - break; + if ($dbType === 'char') { + $fixed = true; } $options = [ @@ -432,7 +280,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) /** * {@inheritDoc} */ - protected function _getPortableViewDefinition($view) + protected function _getPortableViewDefinition(array $view): View { return new View($view['name'], $view['sql']); } @@ -440,7 +288,7 @@ protected function _getPortableViewDefinition($view) /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeysList($tableForeignKeys) + protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array { $list = []; foreach ($tableForeignKeys as $value) { @@ -492,7 +340,7 @@ protected function _getPortableTableForeignKeysList($tableForeignKeys) /** * {@inheritDoc} */ - protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint + protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local'], @@ -511,7 +359,7 @@ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): Fore private function parseColumnCollationFromSQL(string $column, string $sql): ?string { $pattern = '{(?:\W' . preg_quote($column) . '\W|\W' - . preg_quote($this->_platform->quoteSingleIdentifier($column)) + . preg_quote($this->platform->quoteSingleIdentifier($column)) . '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is'; if (preg_match($pattern, $sql, $match) !== 1) { @@ -525,7 +373,7 @@ private function parseTableCommentFromSQL(string $table, string $sql): ?string { $pattern = '/\s* # Allow whitespace characters at start of line CREATE\sTABLE # Match "CREATE TABLE" -(?:\W"' . preg_quote($this->_platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/') +(?:\W"' . preg_quote($this->platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/') . '\W) # Match table name (quoted and unquoted) ( # Start capture (?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s) @@ -540,24 +388,25 @@ private function parseTableCommentFromSQL(string $table, string $sql): ?string return $comment === '' ? null : $comment; } - private function parseColumnCommentFromSQL(string $column, string $sql): ?string + private function parseColumnCommentFromSQL(string $column, string $sql): string { - $pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) + $pattern = '{[\s(,](?:\W' . preg_quote($this->platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column) . '\W)(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i'; if (preg_match($pattern, $sql, $match) !== 1) { - return null; + return ''; } $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); + assert(is_string($comment)); - return $comment === '' ? null : $comment; + return $comment; } /** @throws Exception */ private function getCreateTableSQL(string $table): string { - $sql = $this->_conn->fetchOne( + $sql = $this->connection->fetchOne( <<<'SQL' SELECT sql FROM ( @@ -602,13 +451,11 @@ private function addDetailsToTableForeignKeyColumns(string $table, array $column } /** - * @param string $table - * * @return list> * * @throws Exception */ - private function getForeignKeyDetails($table) + private function getForeignKeyDetails(string $table): array { $createSql = $this->getCreateTableSQL($table); @@ -637,7 +484,7 @@ private function getForeignKeyDetails($table) for ($i = 0, $count = count($match[0]); $i < $count; $i++) { $details[] = [ - 'constraint_name' => isset($names[$i]) && $names[$i] !== '' ? $names[$i] : null, + 'constraint_name' => $names[$i] ?? '', 'deferrable' => isset($deferrable[$i]) && strcasecmp($deferrable[$i], 'deferrable') === 0, 'deferred' => isset($deferred[$i]) && strcasecmp($deferred[$i], 'deferred') === 0, ]; @@ -648,24 +495,7 @@ private function getForeignKeyDetails($table) public function createComparator(): Comparator { - return new SQLite\Comparator($this->_platform); - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function getSchemaSearchPaths() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4821', - 'SqliteSchemaManager::getSchemaSearchPaths() is deprecated.', - ); - - // SQLite does not support schemas or databases - return []; + return new SQLite\Comparator($this->platform); } protected function selectTableNames(string $databaseName): Result @@ -684,7 +514,7 @@ protected function selectTableNames(string $databaseName): Result ORDER BY name SQL; - return $this->_conn->executeQuery($sql); + return $this->connection->executeQuery($sql); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result @@ -709,7 +539,7 @@ protected function selectTableColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, c.cid'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result @@ -734,7 +564,7 @@ protected function selectIndexColumns(string $databaseName, ?string $tableName = $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, i.seq'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result @@ -744,7 +574,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN p.* FROM sqlite_master t JOIN pragma_foreign_key_list(t.name) p - ON p."seq" != "-1" + ON p."seq" != '-1' SQL; $conditions = [ @@ -760,7 +590,7 @@ protected function selectForeignKeyColumns(string $databaseName, ?string $tableN $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, p.id DESC, p.seq'; - return $this->_conn->executeQuery($sql, $params); + return $this->connection->executeQuery($sql, $params); } /** diff --git a/doctrine/dbal/src/Schema/Schema.php b/doctrine/dbal/src/Schema/Schema.php index 703c83c59..25fe4a3c6 100644 --- a/doctrine/dbal/src/Schema/Schema.php +++ b/doctrine/dbal/src/Schema/Schema.php @@ -1,17 +1,21 @@ */ private array $namespaces = []; - /** @var Table[] */ - protected $_tables = []; + /** @var array */ + protected array $_tables = []; - /** @var Sequence[] */ - protected $_sequences = []; + /** @var array */ + protected array $_sequences = []; - /** @var SchemaConfig */ - protected $_schemaConfig; + protected SchemaConfig $_schemaConfig; /** - * @param Table[] $tables - * @param Sequence[] $sequences - * @param string[] $namespaces - * - * @throws SchemaException + * @param array
$tables + * @param array $sequences + * @param array $namespaces */ public function __construct( array $tables = [], array $sequences = [], ?SchemaConfig $schemaConfig = null, - array $namespaces = [] + array $namespaces = [], ) { $schemaConfig ??= new SchemaConfig(); $this->_schemaConfig = $schemaConfig; - $this->_setName($schemaConfig->getName() ?? 'public'); + + $name = $schemaConfig->getName(); + + if ($name !== null) { + $this->_setName($name); + } foreach ($namespaces as $namespace) { $this->createNamespace($namespace); @@ -87,34 +93,13 @@ public function __construct( } } - /** - * @deprecated - * - * @return bool - */ - public function hasExplicitForeignKeyIndexes() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4822', - 'Schema::hasExplicitForeignKeyIndexes() is deprecated.', - ); - - return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); - } - - /** - * @return void - * - * @throws SchemaException - */ - protected function _addTable(Table $table) + protected function _addTable(Table $table): void { $namespaceName = $table->getNamespaceName(); $tableName = $this->normalizeName($table); if (isset($this->_tables[$tableName])) { - throw SchemaException::tableAlreadyExists($tableName); + throw TableAlreadyExists::new($tableName); } if ( @@ -129,18 +114,13 @@ protected function _addTable(Table $table) $table->setSchemaConfig($this->_schemaConfig); } - /** - * @return void - * - * @throws SchemaException - */ - protected function _addSequence(Sequence $sequence) + protected function _addSequence(Sequence $sequence): void { $namespaceName = $sequence->getNamespaceName(); $seqName = $this->normalizeName($sequence); if (isset($this->_sequences[$seqName])) { - throw SchemaException::sequenceAlreadyExists($seqName); + throw SequenceAlreadyExists::new($seqName); } if ( @@ -157,63 +137,67 @@ protected function _addSequence(Sequence $sequence) /** * Returns the namespaces of this schema. * - * @return string[] A list of namespace names. + * @return list A list of namespace names. */ - public function getNamespaces() + public function getNamespaces(): array { - return $this->namespaces; + return array_values($this->namespaces); } /** * Gets all tables of this schema. * - * @return Table[] + * @return list
*/ - public function getTables() + public function getTables(): array { - return $this->_tables; + return array_values($this->_tables); } - /** - * @param string $name - * - * @return Table - * - * @throws SchemaException - */ - public function getTable($name) + public function getTable(string $name): Table { $name = $this->getFullQualifiedAssetName($name); if (! isset($this->_tables[$name])) { - throw SchemaException::tableDoesNotExist($name); + throw TableDoesNotExist::new($name); } return $this->_tables[$name]; } - /** @param string $name */ - private function getFullQualifiedAssetName($name): string + private function getFullQualifiedAssetName(string $name): string { $name = $this->getUnquotedAssetName($name); - if (strpos($name, '.') === false) { + if (! str_contains($name, '.')) { $name = $this->getName() . '.' . $name; } return strtolower($name); } + /** + * The normalized name is qualified and lower-cased. Lower-casing is + * actually wrong, but we have to do it to keep our sanity. If you are + * using database objects that only differentiate in the casing (FOO vs + * Foo) then you will NOT be able to use Doctrine Schema abstraction. + * + * Every non-namespaced element is prefixed with this schema name. + */ private function normalizeName(AbstractAsset $asset): string { - return $asset->getFullQualifiedName($this->getName()); + $name = $asset->getName(); + + if ($asset->getNamespaceName() === null) { + $name = $this->getName() . '.' . $name; + } + + return strtolower($name); } /** * Returns the unquoted representation of a given asset name. - * - * @param string $assetName Quoted or unquoted representation of an asset name. */ - private function getUnquotedAssetName($assetName): string + private function getUnquotedAssetName(string $assetName): string { if ($this->isIdentifierQuoted($assetName)) { return $this->trimQuotes($assetName); @@ -224,12 +208,8 @@ private function getUnquotedAssetName($assetName): string /** * Does this schema have a namespace with the given name? - * - * @param string $name - * - * @return bool */ - public function hasNamespace($name) + public function hasNamespace(string $name): bool { $name = strtolower($this->getUnquotedAssetName($name)); @@ -238,88 +218,48 @@ public function hasNamespace($name) /** * Does this schema have a table with the given name? - * - * @param string $name - * - * @return bool */ - public function hasTable($name) + public function hasTable(string $name): bool { $name = $this->getFullQualifiedAssetName($name); return isset($this->_tables[$name]); } - /** - * Gets all table names, prefixed with a schema name, even the default one if present. - * - * @deprecated Use {@see getTables()} and {@see Table::getName()} instead. - * - * @return string[] - */ - public function getTableNames() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4800', - 'Schema::getTableNames() is deprecated.' - . ' Use Schema::getTables() and Table::getName() instead.', - __METHOD__, - ); - - return array_keys($this->_tables); - } - - /** - * @param string $name - * - * @return bool - */ - public function hasSequence($name) + public function hasSequence(string $name): bool { $name = $this->getFullQualifiedAssetName($name); return isset($this->_sequences[$name]); } - /** - * @param string $name - * - * @return Sequence - * - * @throws SchemaException - */ - public function getSequence($name) + public function getSequence(string $name): Sequence { $name = $this->getFullQualifiedAssetName($name); if (! $this->hasSequence($name)) { - throw SchemaException::sequenceDoesNotExist($name); + throw SequenceDoesNotExist::new($name); } return $this->_sequences[$name]; } - /** @return Sequence[] */ - public function getSequences() + /** @return list */ + public function getSequences(): array { - return $this->_sequences; + return array_values($this->_sequences); } /** * Creates a new namespace. * - * @param string $name The name of the namespace to create. - * - * @return Schema This schema instance. - * - * @throws SchemaException + * @return $this */ - public function createNamespace($name) + public function createNamespace(string $name): self { $unquotedName = strtolower($this->getUnquotedAssetName($name)); if (isset($this->namespaces[$unquotedName])) { - throw SchemaException::namespaceAlreadyExists($unquotedName); + throw NamespaceAlreadyExists::new($unquotedName); } $this->namespaces[$unquotedName] = $name; @@ -329,14 +269,8 @@ public function createNamespace($name) /** * Creates a new table. - * - * @param string $name - * - * @return Table - * - * @throws SchemaException */ - public function createTable($name) + public function createTable(string $name): Table { $table = new Table($name); $this->_addTable($table); @@ -351,14 +285,9 @@ public function createTable($name) /** * Renames a table. * - * @param string $oldName - * @param string $newName - * - * @return Schema - * - * @throws SchemaException + * @return $this */ - public function renameTable($oldName, $newName) + public function renameTable(string $oldName, string $newName): self { $table = $this->getTable($oldName); $table->_setName($newName); @@ -372,13 +301,9 @@ public function renameTable($oldName, $newName) /** * Drops a table from the schema. * - * @param string $name - * - * @return Schema - * - * @throws SchemaException + * @return $this */ - public function dropTable($name) + public function dropTable(string $name): self { $name = $this->getFullQualifiedAssetName($name); $this->getTable($name); @@ -389,16 +314,8 @@ public function dropTable($name) /** * Creates a new sequence. - * - * @param string $name - * @param int $allocationSize - * @param int $initialValue - * - * @return Sequence - * - * @throws SchemaException */ - public function createSequence($name, $allocationSize = 1, $initialValue = 1) + public function createSequence(string $name, int $allocationSize = 1, int $initialValue = 1): Sequence { $seq = new Sequence($name, $allocationSize, $initialValue); $this->_addSequence($seq); @@ -406,12 +323,8 @@ public function createSequence($name, $allocationSize = 1, $initialValue = 1) return $seq; } - /** - * @param string $name - * - * @return Schema - */ - public function dropSequence($name) + /** @return $this */ + public function dropSequence(string $name): self { $name = $this->getFullQualifiedAssetName($name); unset($this->_sequences[$name]); @@ -426,7 +339,7 @@ public function dropSequence($name) * * @throws Exception */ - public function toSql(AbstractPlatform $platform) + public function toSql(AbstractPlatform $platform): array { $builder = new CreateSchemaObjectsSQLBuilder($platform); @@ -437,78 +350,16 @@ public function toSql(AbstractPlatform $platform) * Return an array of necessary SQL queries to drop the schema on the given platform. * * @return list - * - * @throws Exception */ - public function toDropSql(AbstractPlatform $platform) + public function toDropSql(AbstractPlatform $platform): array { $builder = new DropSchemaObjectsSQLBuilder($platform); return $builder->buildSQL($this); } - /** - * @deprecated - * - * @return string[] - * - * @throws SchemaException - */ - public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) - { - $schemaDiff = (new Comparator())->compareSchemas($this, $toSchema); - - return $schemaDiff->toSql($platform); - } - - /** - * @deprecated - * - * @return string[] - * - * @throws SchemaException - */ - public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) - { - $schemaDiff = (new Comparator())->compareSchemas($fromSchema, $this); - - return $schemaDiff->toSql($platform); - } - - /** - * @deprecated - * - * @return void - */ - public function visit(Visitor $visitor) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5435', - 'Schema::visit() is deprecated.', - ); - - $visitor->acceptSchema($this); - - if ($visitor instanceof NamespaceVisitor) { - foreach ($this->namespaces as $namespace) { - $visitor->acceptNamespace($namespace); - } - } - - foreach ($this->_tables as $table) { - $table->visit($visitor); - } - - foreach ($this->_sequences as $sequence) { - $sequence->visit($visitor); - } - } - /** * Cloning a Schema triggers a deep clone of all related assets. - * - * @return void */ public function __clone() { diff --git a/doctrine/dbal/src/Schema/SchemaConfig.php b/doctrine/dbal/src/Schema/SchemaConfig.php index 5e394514b..86cc84f98 100644 --- a/doctrine/dbal/src/Schema/SchemaConfig.php +++ b/doctrine/dbal/src/Schema/SchemaConfig.php @@ -1,98 +1,43 @@ hasExplicitForeignKeyIndexes; - } + protected int $maxIdentifierLength = 63; - /** - * @deprecated - * - * @param bool $flag - * - * @return void - */ - public function setExplicitForeignKeyIndexes($flag) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4822', - 'SchemaConfig::setExplicitForeignKeyIndexes() is deprecated.', - ); + protected ?string $name = null; - $this->hasExplicitForeignKeyIndexes = (bool) $flag; - } + /** @var array */ + protected array $defaultTableOptions = []; - /** - * @param int $length - * - * @return void - */ - public function setMaxIdentifierLength($length) + public function setMaxIdentifierLength(int $length): void { - $this->maxIdentifierLength = (int) $length; + $this->maxIdentifierLength = $length; } - /** @return int */ - public function getMaxIdentifierLength() + public function getMaxIdentifierLength(): int { return $this->maxIdentifierLength; } /** * Gets the default namespace of schema objects. - * - * @return string|null */ - public function getName() + public function getName(): ?string { return $this->name; } /** * Sets the default namespace name of schema objects. - * - * @param string $name The value to set. - * - * @return void */ - public function setName($name) + public function setName(?string $name): void { $this->name = $name; } @@ -101,19 +46,15 @@ public function setName($name) * Gets the default options that are passed to Table instances created with * Schema#createTable(). * - * @return mixed[] + * @return array */ - public function getDefaultTableOptions() + public function getDefaultTableOptions(): array { return $this->defaultTableOptions; } - /** - * @param mixed[] $defaultTableOptions - * - * @return void - */ - public function setDefaultTableOptions(array $defaultTableOptions) + /** @param array $defaultTableOptions */ + public function setDefaultTableOptions(array $defaultTableOptions): void { $this->defaultTableOptions = $defaultTableOptions; } diff --git a/doctrine/dbal/src/Schema/SchemaDiff.php b/doctrine/dbal/src/Schema/SchemaDiff.php index a6a866579..28c014d78 100644 --- a/doctrine/dbal/src/Schema/SchemaDiff.php +++ b/doctrine/dbal/src/Schema/SchemaDiff.php @@ -1,188 +1,95 @@ */ + private readonly array $alteredTables; /** * Constructs an SchemaDiff object. * * @internal The diff can be only instantiated by a {@see Comparator}. * - * @param Table[] $newTables - * @param TableDiff[] $changedTables - * @param Table[] $removedTables - * @param array $createdSchemas - * @param array $droppedSchemas - * @param array $createdSequences - * @param array $alteredSequences - * @param array $droppedSequences + * @param array $createdSchemas + * @param array $droppedSchemas + * @param array
$createdTables + * @param array $alteredTables + * @param array
$droppedTables + * @param array $createdSequences + * @param array $alteredSequences + * @param array $droppedSequences */ public function __construct( - $newTables = [], - $changedTables = [], - $removedTables = [], - ?Schema $fromSchema = null, - $createdSchemas = [], - $droppedSchemas = [], - $createdSequences = [], - $alteredSequences = [], - $droppedSequences = [] + private readonly array $createdSchemas, + private readonly array $droppedSchemas, + private readonly array $createdTables, + array $alteredTables, + private readonly array $droppedTables, + private readonly array $createdSequences, + private readonly array $alteredSequences, + private readonly array $droppedSequences, ) { - $this->newTables = $newTables; - - $this->changedTables = array_filter($changedTables, static function (TableDiff $diff): bool { + $this->alteredTables = array_filter($alteredTables, static function (TableDiff $diff): bool { return ! $diff->isEmpty(); }); - - $this->removedTables = $removedTables; - $this->fromSchema = $fromSchema; - $this->newNamespaces = $createdSchemas; - $this->removedNamespaces = $droppedSchemas; - $this->newSequences = $createdSequences; - $this->changedSequences = $alteredSequences; - $this->removedSequences = $droppedSequences; } /** @return array */ public function getCreatedSchemas(): array { - return $this->newNamespaces; + return $this->createdSchemas; } /** @return array */ public function getDroppedSchemas(): array { - return $this->removedNamespaces; + return $this->droppedSchemas; } /** @return array
*/ public function getCreatedTables(): array { - return $this->newTables; + return $this->createdTables; } /** @return array */ public function getAlteredTables(): array { - return $this->changedTables; + return $this->alteredTables; } /** @return array
*/ public function getDroppedTables(): array { - return $this->removedTables; + return $this->droppedTables; } /** @return array */ public function getCreatedSequences(): array { - return $this->newSequences; + return $this->createdSequences; } /** @return array */ public function getAlteredSequences(): array { - return $this->changedSequences; + return $this->alteredSequences; } /** @return array */ public function getDroppedSequences(): array { - return $this->removedSequences; + return $this->droppedSequences; } /** @@ -190,105 +97,13 @@ public function getDroppedSequences(): array */ public function isEmpty(): bool { - return count($this->newNamespaces) === 0 - && count($this->removedNamespaces) === 0 - && count($this->newTables) === 0 - && count($this->changedTables) === 0 - && count($this->removedTables) === 0 - && count($this->newSequences) === 0 - && count($this->changedSequences) === 0 - && count($this->removedSequences) === 0; - } - - /** - * The to save sql mode ensures that the following things don't happen: - * - * 1. Tables are deleted - * 2. Sequences are deleted - * 3. Foreign Keys which reference tables that would otherwise be deleted. - * - * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. - * - * @deprecated - * - * @return list - */ - public function toSaveSql(AbstractPlatform $platform) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5766', - '%s is deprecated.', - __METHOD__, - ); - - return $this->_toSql($platform, true); - } - - /** - * @deprecated Use {@link AbstractPlatform::getAlterSchemaSQL()} instead. - * - * @return list - */ - public function toSql(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5766', - '%s is deprecated. Use AbstractPlatform::getAlterSchemaSQL() instead.', - __METHOD__, - ); - - return $this->_toSql($platform, false); - } - - /** - * @param bool $saveMode - * - * @return list - */ - protected function _toSql(AbstractPlatform $platform, $saveMode = false) - { - $sql = []; - - if ($platform->supportsSchemas()) { - foreach ($this->getCreatedSchemas() as $schema) { - $sql[] = $platform->getCreateSchemaSQL($schema); - } - } - - if ($platform->supportsForeignKeyConstraints() && $saveMode === false) { - foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { - $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTable()); - } - } - - if ($platform->supportsSequences() === true) { - foreach ($this->getAlteredSequences() as $sequence) { - $sql[] = $platform->getAlterSequenceSQL($sequence); - } - - if ($saveMode === false) { - foreach ($this->getDroppedSequences() as $sequence) { - $sql[] = $platform->getDropSequenceSQL($sequence); - } - } - - foreach ($this->getCreatedSequences() as $sequence) { - $sql[] = $platform->getCreateSequenceSQL($sequence); - } - } - - $sql = array_merge($sql, $platform->getCreateTablesSQL($this->getCreatedTables())); - - if ($saveMode === false) { - $sql = array_merge($sql, $platform->getDropTablesSQL($this->getDroppedTables())); - } - - foreach ($this->getAlteredTables() as $tableDiff) { - $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); - } - - return $sql; + return count($this->createdSchemas) === 0 + && count($this->droppedSchemas) === 0 + && count($this->createdTables) === 0 + && count($this->alteredTables) === 0 + && count($this->droppedTables) === 0 + && count($this->createdSequences) === 0 + && count($this->alteredSequences) === 0 + && count($this->droppedSequences) === 0; } } diff --git a/doctrine/dbal/src/Schema/SchemaException.php b/doctrine/dbal/src/Schema/SchemaException.php index 4ec091f8d..43dd2ad86 100644 --- a/doctrine/dbal/src/Schema/SchemaException.php +++ b/doctrine/dbal/src/Schema/SchemaException.php @@ -1,204 +1,12 @@ _setName($name); $this->setAllocationSize($allocationSize); $this->setInitialValue($initialValue); - $this->cache = $cache; } - /** @return int */ - public function getAllocationSize() + public function getAllocationSize(): int { return $this->allocationSize; } - /** @return int */ - public function getInitialValue() + public function getInitialValue(): int { return $this->initialValue; } - /** @return int|null */ - public function getCache() + public function getCache(): ?int { return $this->cache; } - /** - * @param int $allocationSize - * - * @return Sequence - */ - public function setAllocationSize($allocationSize) + public function setAllocationSize(int $allocationSize): self { - if ($allocationSize > 0) { - $this->allocationSize = $allocationSize; - } else { - $this->allocationSize = 1; - } + $this->allocationSize = $allocationSize; return $this; } - /** - * @param int $initialValue - * - * @return Sequence - */ - public function setInitialValue($initialValue) + public function setInitialValue(int $initialValue): self { - if ($initialValue > 0) { - $this->initialValue = $initialValue; - } else { - $this->initialValue = 1; - } + $this->initialValue = $initialValue; return $this; } - /** - * @param int $cache - * - * @return Sequence - */ - public function setCache($cache) + public function setCache(int $cache): self { $this->cache = $cache; @@ -103,10 +68,8 @@ public function setCache($cache) * * This is used inside the comparator to not report sequences as missing, * when the "from" schema implicitly creates the sequences. - * - * @return bool */ - public function isAutoIncrementsFor(Table $table) + public function isAutoIncrementsFor(Table $table): bool { $primaryKey = $table->getPrimaryKey(); @@ -132,20 +95,4 @@ public function isAutoIncrementsFor(Table $table) return $tableSequenceName === $sequenceName; } - - /** - * @deprecated - * - * @return void - */ - public function visit(Visitor $visitor) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5435', - 'Sequence::visit() is deprecated.', - ); - - $visitor->acceptSequence($this); - } } diff --git a/doctrine/dbal/src/Schema/Table.php b/doctrine/dbal/src/Schema/Table.php index ce4cc3260..cc7f04d2c 100644 --- a/doctrine/dbal/src/Schema/Table.php +++ b/doctrine/dbal/src/Schema/Table.php @@ -1,63 +1,61 @@ [], ]; - /** @var SchemaConfig|null */ - protected $_schemaConfig; - - /** @var Index[] */ - private array $implicitIndexes = []; + protected ?SchemaConfig $_schemaConfig = null; /** - * @param Column[] $columns - * @param Index[] $indexes - * @param UniqueConstraint[] $uniqueConstraints - * @param ForeignKeyConstraint[] $fkConstraints - * @param mixed[] $options - * - * @throws SchemaException - * @throws Exception + * @param array $columns + * @param array $indexes + * @param array $uniqueConstraints + * @param array $fkConstraints + * @param array $options */ public function __construct( string $name, @@ -65,7 +63,7 @@ public function __construct( array $indexes = [], array $uniqueConstraints = [], array $fkConstraints = [], - array $options = [] + array $options = [], ) { if ($name === '') { throw InvalidTableName::new($name); @@ -85,42 +83,26 @@ public function __construct( $this->_addUniqueConstraint($uniqueConstraint); } - foreach ($fkConstraints as $constraint) { - $this->_addForeignKeyConstraint($constraint); + foreach ($fkConstraints as $fkConstraint) { + $this->_addForeignKeyConstraint($fkConstraint); } $this->_options = array_merge($this->_options, $options); } - /** @return void */ - public function setSchemaConfig(SchemaConfig $schemaConfig) + public function setSchemaConfig(SchemaConfig $schemaConfig): void { $this->_schemaConfig = $schemaConfig; } - /** @return int */ - protected function _getMaxIdentifierLength() - { - if ($this->_schemaConfig instanceof SchemaConfig) { - return $this->_schemaConfig->getMaxIdentifierLength(); - } - - return 63; - } - /** * Sets the Primary Key. * - * @param string[] $columnNames - * @param string|false $indexName - * - * @return self - * - * @throws SchemaException + * @param array $columnNames */ - public function setPrimaryKey(array $columnNames, $indexName = false) + public function setPrimaryKey(array $columnNames, ?string $indexName = null): self { - if ($indexName === false) { + if ($indexName === null) { $indexName = 'primary'; } @@ -135,55 +117,49 @@ public function setPrimaryKey(array $columnNames, $indexName = false) } /** - * @param string[] $columnNames - * @param string[] $flags - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException + * @param array $columnNames + * @param array $flags + * @param array $options */ - public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) - { + public function addUniqueConstraint( + array $columnNames, + ?string $indexName = null, + array $flags = [], + array $options = [], + ): self { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), - 'idx', + 'uniq', $this->_getMaxIdentifierLength(), ); - return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); + return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); } /** - * @param string[] $columnNames - * @param string[] $flags - * @param mixed[] $options - * - * @return self + * @param array $columnNames + * @param array $flags + * @param array $options */ - public function addUniqueConstraint( + public function addIndex( array $columnNames, ?string $indexName = null, array $flags = [], - array $options = [] - ): Table { + array $options = [], + ): self { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), - 'uniq', + 'idx', $this->_getMaxIdentifierLength(), ); - return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); + return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); } /** * Drops the primary key from this table. - * - * @return void - * - * @throws SchemaException */ - public function dropPrimaryKey() + public function dropPrimaryKey(): void { if ($this->_primaryKeyName === null) { return; @@ -195,34 +171,23 @@ public function dropPrimaryKey() /** * Drops an index from this table. - * - * @param string $name The index name. - * - * @return void - * - * @throws SchemaException If the index does not exist. */ - public function dropIndex($name) + public function dropIndex(string $name): void { $name = $this->normalizeIdentifier($name); if (! $this->hasIndex($name)) { - throw SchemaException::indexDoesNotExist($name, $this->_name); + throw IndexDoesNotExist::new($name, $this->_name); } unset($this->_indexes[$name]); } /** - * @param string[] $columnNames - * @param string|null $indexName - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException + * @param array $columnNames + * @param array $options */ - public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) + public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []): self { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), @@ -237,15 +202,10 @@ public function addUniqueIndex(array $columnNames, $indexName = null, array $opt * Renames an index. * * @param string $oldName The name of the index to rename from. - * @param string|null $newName The name of the index to rename to. - * If null is given, the index name will be auto-generated. - * - * @return self This table instance. - * - * @throws SchemaException If no index exists for the given current name - * or if an index with the given new name already exists on this table. + * @param string|null $newName The name of the index to rename to. If null is given, the index name + * will be auto-generated. */ - public function renameIndex($oldName, $newName = null) + public function renameIndex(string $oldName, ?string $newName = null): self { $oldName = $this->normalizeIdentifier($oldName); $normalizedNewName = $this->normalizeIdentifier($newName); @@ -255,11 +215,11 @@ public function renameIndex($oldName, $newName = null) } if (! $this->hasIndex($oldName)) { - throw SchemaException::indexDoesNotExist($oldName, $this->_name); + throw IndexDoesNotExist::new($oldName, $this->_name); } if ($this->hasIndex($normalizedNewName)) { - throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name); + throw IndexAlreadyExists::new($normalizedNewName, $this->_name); } $oldIndex = $this->_indexes[$oldName]; @@ -267,7 +227,7 @@ public function renameIndex($oldName, $newName = null) if ($oldIndex->isPrimary()) { $this->dropPrimaryKey(); - return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false); + return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? null); } unset($this->_indexes[$oldName]); @@ -282,11 +242,9 @@ public function renameIndex($oldName, $newName = null) /** * Checks if an index begins in the order of the given columns. * - * @param string[] $columnNames - * - * @return bool + * @param array $columnNames */ - public function columnsAreIndexed(array $columnNames) + public function columnsAreIndexed(array $columnNames): bool { foreach ($this->getIndexes() as $index) { if ($index->spansColumns($columnNames)) { @@ -297,47 +255,8 @@ public function columnsAreIndexed(array $columnNames) return false; } - /** - * @param string[] $columnNames - * @param string $indexName - * @param bool $isUnique - * @param bool $isPrimary - * @param string[] $flags - * @param mixed[] $options - * - * @throws SchemaException - */ - private function _createIndex( - array $columnNames, - $indexName, - $isUnique, - $isPrimary, - array $flags = [], - array $options = [] - ): Index { - if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { - throw SchemaException::indexNameInvalid($indexName); - } - - foreach ($columnNames as $columnName) { - if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); - } - } - - return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); - } - - /** - * @param string $name - * @param string $typeName - * @param mixed[] $options - * - * @return Column - * - * @throws SchemaException - */ - public function addColumn($name, $typeName, array $options = []) + /** @param array $options */ + public function addColumn(string $name, string $typeName, array $options = []): Column { $column = new Column($name, Type::getType($typeName), $options); @@ -346,39 +265,8 @@ public function addColumn($name, $typeName, array $options = []) return $column; } - /** - * Change Column Details. - * - * @deprecated Use {@link modifyColumn()} instead. - * - * @param string $name - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function changeColumn($name, array $options) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5747', - '%s is deprecated. Use modifyColumn() instead.', - __METHOD__, - ); - - return $this->modifyColumn($name, $options); - } - - /** - * @param string $name - * @param mixed[] $options - * - * @return self - * - * @throws SchemaException - */ - public function modifyColumn($name, array $options) + /** @param array $options */ + public function modifyColumn(string $name, array $options): self { $column = $this->getColumn($name); $column->setOptions($options); @@ -388,12 +276,8 @@ public function modifyColumn($name, array $options) /** * Drops a Column from the Table. - * - * @param string $name - * - * @return self */ - public function dropColumn($name) + public function dropColumn(string $name): self { $name = $this->normalizeIdentifier($name); @@ -407,46 +291,32 @@ public function dropColumn($name) * * Name is inferred from the local columns. * - * @param Table|string $foreignTable Table schema instance or table name - * @param string[] $localColumnNames - * @param string[] $foreignColumnNames - * @param mixed[] $options - * @param string|null $name - * - * @return self - * - * @throws SchemaException + * @param array $localColumnNames + * @param array $foreignColumnNames + * @param array $options */ public function addForeignKeyConstraint( - $foreignTable, + string $foreignTableName, array $localColumnNames, array $foreignColumnNames, array $options = [], - $name = null - ) { + ?string $name = null, + ): self { $name ??= $this->_generateIdentifierName( array_merge([$this->getName()], $localColumnNames), 'fk', $this->_getMaxIdentifierLength(), ); - if ($foreignTable instanceof Table) { - foreach ($foreignColumnNames as $columnName) { - if (! $foreignTable->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); - } - } - } - foreach ($localColumnNames as $columnName) { if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); + throw ColumnDoesNotExist::new($columnName, $this->_name); } } $constraint = new ForeignKeyConstraint( $localColumnNames, - $foreignTable, + $foreignTableName, $foreignColumnNames, $name, $options, @@ -455,161 +325,17 @@ public function addForeignKeyConstraint( return $this->_addForeignKeyConstraint($constraint); } - /** - * @param string $name - * @param mixed $value - * - * @return self - */ - public function addOption($name, $value) + public function addOption(string $name, mixed $value): self { $this->_options[$name] = $value; return $this; } - /** - * @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->isFulfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { - continue; - } - - $replacedImplicitIndexes[] = $name; - } - - if ( - (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || - ($this->_primaryKeyName !== null && $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): Table - { - $mergedNames = array_merge([$this->getName()], $constraint->getColumns()); - $name = strlen($constraint->getName()) > 0 - ? $constraint->getName() - : $this->_generateIdentifierName($mergedNames, '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($mergedNames, 'idx', $this->_getMaxIdentifierLength()); - - $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); - - foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFulfilledBy($existingIndex)) { - return $this; - } - } - - $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; - - return $this; - } - - /** @return self */ - protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) - { - $constraint->setLocalTable($this); - - if (strlen($constraint->getName()) > 0) { - $name = $constraint->getName(); - } else { - $name = $this->_generateIdentifierName( - array_merge([$this->getName()], $constraint->getLocalColumns()), - 'fk', - $this->_getMaxIdentifierLength(), - ); - } - - $name = $this->normalizeIdentifier($name); - - $this->_fkConstraints[$name] = $constraint; - - /* Add an implicit index (defined by the DBAL) on the foreign key - columns. If there is already a user-defined index that fulfills these - 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); - - foreach ($this->_indexes as $existingIndex) { - if ($indexCandidate->isFulfilledBy($existingIndex)) { - return $this; - } - } - - $this->_addIndex($indexCandidate); - $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; - - return $this; - } - /** * Returns whether this table has a foreign key constraint with the given name. - * - * @param string $name - * - * @return bool */ - public function hasForeignKey($name) + public function hasForeignKey(string $name): bool { $name = $this->normalizeIdentifier($name); @@ -618,19 +344,13 @@ public function hasForeignKey($name) /** * Returns the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return ForeignKeyConstraint - * - * @throws SchemaException If the foreign key does not exist. */ - public function getForeignKey($name) + public function getForeignKey(string $name): ForeignKeyConstraint { $name = $this->normalizeIdentifier($name); if (! $this->hasForeignKey($name)) { - throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); + throw ForeignKeyDoesNotExist::new($name, $this->_name); } return $this->_fkConstraints[$name]; @@ -638,19 +358,13 @@ public function getForeignKey($name) /** * Removes the foreign key constraint with the given name. - * - * @param string $name The constraint name. - * - * @return void - * - * @throws SchemaException */ - public function removeForeignKey($name) + public function removeForeignKey(string $name): void { $name = $this->normalizeIdentifier($name); if (! $this->hasForeignKey($name)) { - throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); + throw ForeignKeyDoesNotExist::new($name, $this->_name); } unset($this->_fkConstraints[$name]); @@ -668,15 +382,13 @@ public function hasUniqueConstraint(string $name): bool /** * Returns the unique constraint with the given name. - * - * @throws SchemaException If the unique constraint does not exist. */ public function getUniqueConstraint(string $name): UniqueConstraint { $name = $this->normalizeIdentifier($name); if (! $this->hasUniqueConstraint($name)) { - throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); + throw UniqueConstraintDoesNotExist::new($name, $this->_name); } return $this->uniqueConstraints[$name]; @@ -684,84 +396,32 @@ public function getUniqueConstraint(string $name): UniqueConstraint /** * Removes the unique constraint with the given name. - * - * @throws SchemaException If the unique constraint does not exist. */ public function removeUniqueConstraint(string $name): void { $name = $this->normalizeIdentifier($name); if (! $this->hasUniqueConstraint($name)) { - throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); + throw UniqueConstraintDoesNotExist::new($name, $this->_name); } unset($this->uniqueConstraints[$name]); } /** - * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) + * Returns the list of table columns. * - * @return Column[] + * @return list */ - public function getColumns() + public function getColumns(): array { - $primaryKeyColumns = $this->getPrimaryKey() !== null ? $this->getPrimaryKeyColumns() : []; - $foreignKeyColumns = $this->getForeignKeyColumns(); - $remainderColumns = $this->filterColumns( - array_merge(array_keys($primaryKeyColumns), array_keys($foreignKeyColumns)), - true, - ); - - return array_merge($primaryKeyColumns, $foreignKeyColumns, $remainderColumns); - } - - /** - * Returns the foreign key columns - * - * @deprecated Use {@see getForeignKey()} and {@see ForeignKeyConstraint::getLocalColumns()} instead. - * - * @return Column[] - */ - public function getForeignKeyColumns() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5731', - '%s is deprecated. Use getForeignKey() and ForeignKeyConstraint::getLocalColumns() instead.', - __METHOD__, - ); - - $foreignKeyColumns = []; - - foreach ($this->getForeignKeys() as $foreignKey) { - $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getLocalColumns()); - } - - return $this->filterColumns($foreignKeyColumns); - } - - /** - * Returns only columns that have specified names - * - * @param string[] $columnNames - * - * @return Column[] - */ - private function filterColumns(array $columnNames, bool $reverse = false): array - { - return array_filter($this->_columns, static function (string $columnName) use ($columnNames, $reverse): bool { - return in_array($columnName, $columnNames, true) !== $reverse; - }, ARRAY_FILTER_USE_KEY); + return array_values($this->_columns); } /** * Returns whether this table has a Column with the given name. - * - * @param string $name The column name. - * - * @return bool */ - public function hasColumn($name) + public function hasColumn(string $name): bool { $name = $this->normalizeIdentifier($name); @@ -770,19 +430,13 @@ public function hasColumn($name) /** * Returns the Column with the given name. - * - * @param string $name The column name. - * - * @return Column - * - * @throws SchemaException If the column does not exist. */ - public function getColumn($name) + public function getColumn(string $name): Column { $name = $this->normalizeIdentifier($name); if (! $this->hasColumn($name)) { - throw SchemaException::columnDoesNotExist($name, $this->_name); + throw ColumnDoesNotExist::new($name, $this->_name); } return $this->_columns[$name]; @@ -790,10 +444,8 @@ public function getColumn($name) /** * Returns the primary key. - * - * @return Index|null The primary key, or null if this Table has no primary key. */ - public function getPrimaryKey() + public function getPrimaryKey(): ?Index { if ($this->_primaryKeyName !== null) { return $this->getIndex($this->_primaryKeyName); @@ -802,60 +454,10 @@ public function getPrimaryKey() return null; } - /** - * Returns the primary key columns. - * - * @deprecated Use {@see getPrimaryKey()} and {@see Index::getColumns()} instead. - * - * @return Column[] - * - * @throws Exception - */ - public function getPrimaryKeyColumns() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5731', - '%s is deprecated. Use getPrimaryKey() and Index::getColumns() instead.', - __METHOD__, - ); - - $primaryKey = $this->getPrimaryKey(); - - if ($primaryKey === null) { - throw new Exception('Table ' . $this->getName() . ' has no primary key.'); - } - - return $this->filterColumns($primaryKey->getColumns()); - } - - /** - * Returns whether this table has a primary key. - * - * @deprecated Use {@see getPrimaryKey()} instead. - * - * @return bool - */ - public function hasPrimaryKey() - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5731', - '%s is deprecated. Use getPrimaryKey() instead.', - __METHOD__, - ); - - return $this->_primaryKeyName !== null && $this->hasIndex($this->_primaryKeyName); - } - /** * Returns whether this table has an Index with the given name. - * - * @param string $name The index name. - * - * @return bool */ - public function hasIndex($name) + public function hasIndex(string $name): bool { $name = $this->normalizeIdentifier($name); @@ -864,25 +466,20 @@ public function hasIndex($name) /** * Returns the Index with the given name. - * - * @param string $name The index name. - * - * @return Index - * - * @throws SchemaException If the index does not exist. */ - public function getIndex($name) + public function getIndex(string $name): Index { $name = $this->normalizeIdentifier($name); + if (! $this->hasIndex($name)) { - throw SchemaException::indexDoesNotExist($name, $this->_name); + throw IndexDoesNotExist::new($name, $this->_name); } return $this->_indexes[$name]; } - /** @return Index[] */ - public function getIndexes() + /** @return array */ + public function getIndexes(): array { return $this->_indexes; } @@ -890,7 +487,7 @@ public function getIndexes() /** * Returns the unique constraints. * - * @return UniqueConstraint[] + * @return array */ public function getUniqueConstraints(): array { @@ -900,122 +497,181 @@ public function getUniqueConstraints(): array /** * Returns the foreign key constraints. * - * @return ForeignKeyConstraint[] + * @return array */ - public function getForeignKeys() + public function getForeignKeys(): array { return $this->_fkConstraints; } - /** - * @param string $name - * - * @return bool - */ - public function hasOption($name) + public function hasOption(string $name): bool { return isset($this->_options[$name]); } - /** - * @param string $name - * - * @return mixed - */ - public function getOption($name) + public function getOption(string $name): mixed { - return $this->_options[$name]; + return $this->_options[$name] ?? null; } - /** @return mixed[] */ - public function getOptions() + /** @return array */ + public function getOptions(): array { return $this->_options; } /** - * @deprecated - * - * @return void - * - * @throws SchemaException + * Clone of a Table triggers a deep clone of all affected assets. */ - public function visit(Visitor $visitor) + public function __clone() { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5435', - 'Table::visit() is deprecated.', - ); - - $visitor->acceptTable($this); + foreach ($this->_columns as $k => $column) { + $this->_columns[$k] = clone $column; + } - foreach ($this->getColumns() as $column) { - $visitor->acceptColumn($this, $column); + foreach ($this->_indexes as $k => $index) { + $this->_indexes[$k] = clone $index; } - foreach ($this->getIndexes() as $index) { - $visitor->acceptIndex($this, $index); + foreach ($this->_fkConstraints as $k => $fk) { + $this->_fkConstraints[$k] = clone $fk; } + } - foreach ($this->getForeignKeys() as $constraint) { - $visitor->acceptForeignKey($this, $constraint); + protected function _getMaxIdentifierLength(): int + { + return $this->_schemaConfig instanceof SchemaConfig + ? $this->_schemaConfig->getMaxIdentifierLength() + : 63; + } + + protected function _addColumn(Column $column): void + { + $columnName = $column->getName(); + $columnName = $this->normalizeIdentifier($columnName); + + if (isset($this->_columns[$columnName])) { + throw ColumnAlreadyExists::new($this->getName(), $columnName); } + + $this->_columns[$columnName] = $column; } /** - * Clone of a Table triggers a deep clone of all affected assets. - * - * @return void + * Adds an index to the table. */ - public function __clone() + protected function _addIndex(Index $indexCandidate): self { - foreach ($this->_columns as $k => $column) { - $this->_columns[$k] = clone $column; + $indexName = $indexCandidate->getName(); + $indexName = $this->normalizeIdentifier($indexName); + $replacedImplicitIndexes = []; + + foreach ($this->implicitIndexes as $name => $implicitIndex) { + if (! $implicitIndex->isFulfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { + continue; + } + + $replacedImplicitIndexes[] = $name; } - foreach ($this->_indexes as $k => $index) { - $this->_indexes[$k] = clone $index; + if ( + (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || + ($this->_primaryKeyName !== null && $indexCandidate->isPrimary()) + ) { + throw IndexAlreadyExists::new($indexName, $this->_name); } - foreach ($this->_fkConstraints as $k => $fk) { - $this->_fkConstraints[$k] = clone $fk; - $this->_fkConstraints[$k]->setLocalTable($this); + foreach ($replacedImplicitIndexes as $name) { + unset($this->_indexes[$name], $this->implicitIndexes[$name]); + } + + if ($indexCandidate->isPrimary()) { + $this->_primaryKeyName = $indexName; } + + $this->_indexes[$indexName] = $indexCandidate; + + return $this; } - /** - * @param string[] $columnNames - * @param string[] $flags - * @param mixed[] $options - * - * @throws SchemaException - */ - private function _createUniqueConstraint( - array $columnNames, - string $indexName, - array $flags = [], - array $options = [] - ): UniqueConstraint { - if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { - throw SchemaException::indexNameInvalid($indexName); + protected function _addUniqueConstraint(UniqueConstraint $constraint): self + { + $name = $constraint->getName() !== '' + ? $constraint->getName() + : $this->_generateIdentifierName( + array_merge((array) $this->getName(), $constraint->getColumns()), + '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->isFulfilledBy($existingIndex)) { + return $this; + } } - foreach ($columnNames as $columnName) { - if (! $this->hasColumn($columnName)) { - throw SchemaException::columnDoesNotExist($columnName, $this->_name); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; + } + + protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint): self + { + $name = $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->getLocalColumns()), + 'idx', + $this->_getMaxIdentifierLength(), + ); + + $indexCandidate = $this->_createIndex($constraint->getLocalColumns(), $indexName, false, false); + + foreach ($this->_indexes as $existingIndex) { + if ($indexCandidate->isFulfilledBy($existingIndex)) { + return $this; } } - return new UniqueConstraint($indexName, $columnNames, $flags, $options); + $this->_addIndex($indexCandidate); + $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; + + return $this; } /** * Normalizes a given identifier. * * Trims quotes and lowercases the given identifier. - * - * @return string The normalized identifier. */ private function normalizeIdentifier(?string $identifier): string { @@ -1026,7 +682,7 @@ private function normalizeIdentifier(?string $identifier): string return $this->trimQuotes(strtolower($identifier)); } - public function setComment(?string $comment): self + public function setComment(string $comment): self { // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options. $this->addOption('comment', $comment); @@ -1038,4 +694,60 @@ public function getComment(): ?string { return $this->_options['comment'] ?? null; } + + /** + * @param array $columns + * @param array $flags + * @param array $options + */ + private function _createUniqueConstraint( + array $columns, + string $indexName, + array $flags = [], + array $options = [], + ): UniqueConstraint { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { + throw IndexNameInvalid::new($indexName); + } + + foreach ($columns as $index => $value) { + if (is_string($index)) { + $columnName = $index; + } else { + $columnName = $value; + } + + if (! $this->hasColumn($columnName)) { + throw ColumnDoesNotExist::new($columnName, $this->_name); + } + } + + return new UniqueConstraint($indexName, $columns, $flags, $options); + } + + /** + * @param array $columns + * @param array $flags + * @param array $options + */ + private function _createIndex( + array $columns, + string $indexName, + bool $isUnique, + bool $isPrimary, + array $flags = [], + array $options = [], + ): Index { + if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { + throw IndexNameInvalid::new($indexName); + } + + foreach ($columns as $columnName) { + if (! $this->hasColumn($columnName)) { + throw ColumnDoesNotExist::new($columnName, $this->_name); + } + } + + return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options); + } } diff --git a/doctrine/dbal/src/Schema/TableDiff.php b/doctrine/dbal/src/Schema/TableDiff.php index 9aaf9e770..1cd59e841 100644 --- a/doctrine/dbal/src/Schema/TableDiff.php +++ b/doctrine/dbal/src/Schema/TableDiff.php @@ -1,12 +1,10 @@ $addedColumns - * @param array $modifiedColumns - * @param array $droppedColumns - * @param array $addedIndexes - * @param array $changedIndexes - * @param array $removedIndexes - * @param list $addedForeignKeys - * @param list $changedForeignKeys - * @param list $removedForeignKeys - * @param array $renamedColumns - * @param array $renamedIndexes + * @param array $droppedForeignKeys + * @param array $addedColumns + * @param array $modifiedColumns + * @param array $droppedColumns + * @param array $renamedColumns + * @param array $addedIndexes + * @param array $modifiedIndexes + * @param array $droppedIndexes + * @param array $renamedIndexes + * @param array $addedForeignKeys + * @param array $modifiedForeignKeys */ public function __construct( - $tableName, - $addedColumns = [], - $modifiedColumns = [], - $droppedColumns = [], - $addedIndexes = [], - $changedIndexes = [], - $removedIndexes = [], - ?Table $fromTable = null, - $addedForeignKeys = [], - $changedForeignKeys = [], - $removedForeignKeys = [], - $renamedColumns = [], - $renamedIndexes = [] + private readonly Table $oldTable, + private readonly array $addedColumns, + private readonly array $modifiedColumns, + private readonly array $droppedColumns, + private readonly array $renamedColumns, + private array $addedIndexes, + private readonly array $modifiedIndexes, + private array $droppedIndexes, + private readonly array $renamedIndexes, + private readonly array $addedForeignKeys, + private readonly array $modifiedForeignKeys, + private readonly array $droppedForeignKeys, ) { - $this->name = $tableName; - $this->addedColumns = $addedColumns; - $this->changedColumns = $modifiedColumns; - $this->renamedColumns = $renamedColumns; - $this->removedColumns = $droppedColumns; - $this->addedIndexes = $addedIndexes; - $this->changedIndexes = $changedIndexes; - $this->renamedIndexes = $renamedIndexes; - $this->removedIndexes = $removedIndexes; - $this->addedForeignKeys = $addedForeignKeys; - $this->changedForeignKeys = $changedForeignKeys; - $this->removedForeignKeys = $removedForeignKeys; - - if ($fromTable === null) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5678', - 'Not passing the $fromTable to %s is deprecated.', - __METHOD__, - ); - } - - $this->fromTable = $fromTable; } - /** - * @deprecated Use {@see getOldTable()} instead. - * - * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. - * - * @return Identifier - */ - public function getName(AbstractPlatform $platform) - { - return new Identifier( - $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name, - ); - } - - /** - * @deprecated Rename tables via {@link AbstractSchemaManager::renameTable()} instead. - * - * @return Identifier|false - */ - public function getNewName() - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5663', - '%s is deprecated. Rename tables via AbstractSchemaManager::renameTable() instead.', - __METHOD__, - ); - - if ($this->newName === false) { - return false; - } - - return new Identifier($this->newName); - } - - public function getOldTable(): ?Table + public function getOldTable(): Table { - return $this->fromTable; + return $this->oldTable; } - /** @return list */ + /** @return array */ public function getAddedColumns(): array { - return array_values($this->addedColumns); + return $this->addedColumns; } - /** @return list */ + /** @return array */ public function getModifiedColumns(): array { - return array_values($this->changedColumns); + return $this->modifiedColumns; } - /** @return list */ + /** @return array */ public function getDroppedColumns(): array { - return array_values($this->removedColumns); + return $this->droppedColumns; } /** @return array */ @@ -256,10 +74,10 @@ public function getRenamedColumns(): array return $this->renamedColumns; } - /** @return list */ + /** @return array */ public function getAddedIndexes(): array { - return array_values($this->addedIndexes); + return $this->addedIndexes; } /** @@ -279,13 +97,13 @@ static function (Index $addedIndex) use ($index): bool { /** @return array */ public function getModifiedIndexes(): array { - return array_values($this->changedIndexes); + return $this->modifiedIndexes; } - /** @return list */ + /** @return array */ public function getDroppedIndexes(): array { - return array_values($this->removedIndexes); + return $this->droppedIndexes; } /** @@ -294,10 +112,10 @@ public function getDroppedIndexes(): array */ public function unsetDroppedIndex(Index $index): void { - $this->removedIndexes = array_filter( - $this->removedIndexes, - static function (Index $removedIndex) use ($index): bool { - return $removedIndex !== $index; + $this->droppedIndexes = array_filter( + $this->droppedIndexes, + static function (Index $droppedIndex) use ($index): bool { + return $droppedIndex !== $index; }, ); } @@ -308,37 +126,22 @@ public function getRenamedIndexes(): array return $this->renamedIndexes; } - /** @return list */ + /** @return array */ public function getAddedForeignKeys(): array { return $this->addedForeignKeys; } - /** @return list */ + /** @return array */ public function getModifiedForeignKeys(): array { - return $this->changedForeignKeys; + return $this->modifiedForeignKeys; } - /** @return list */ + /** @return array */ public function getDroppedForeignKeys(): array { - return $this->removedForeignKeys; - } - - /** - * @internal This method exists only for compatibility with the current implementation of the schema comparator. - * - * @param ForeignKeyConstraint|string $foreignKey - */ - public function unsetDroppedForeignKey($foreignKey): void - { - $this->removedForeignKeys = array_filter( - $this->removedForeignKeys, - static function ($removedForeignKey) use ($foreignKey): bool { - return $removedForeignKey !== $foreignKey; - }, - ); + return $this->droppedForeignKeys; } /** @@ -347,15 +150,15 @@ static function ($removedForeignKey) use ($foreignKey): bool { public function isEmpty(): bool { return count($this->addedColumns) === 0 - && count($this->changedColumns) === 0 - && count($this->removedColumns) === 0 + && count($this->modifiedColumns) === 0 + && count($this->droppedColumns) === 0 && count($this->renamedColumns) === 0 && count($this->addedIndexes) === 0 - && count($this->changedIndexes) === 0 - && count($this->removedIndexes) === 0 + && count($this->modifiedIndexes) === 0 + && count($this->droppedIndexes) === 0 && count($this->renamedIndexes) === 0 && count($this->addedForeignKeys) === 0 - && count($this->changedForeignKeys) === 0 - && count($this->removedForeignKeys) === 0; + && count($this->modifiedForeignKeys) === 0 + && count($this->droppedForeignKeys) === 0; } } diff --git a/doctrine/dbal/src/Schema/UniqueConstraint.php b/doctrine/dbal/src/Schema/UniqueConstraint.php index f353f303a..a33d446e0 100644 --- a/doctrine/dbal/src/Schema/UniqueConstraint.php +++ b/doctrine/dbal/src/Schema/UniqueConstraint.php @@ -1,5 +1,7 @@ Identifier) - * - * @var Identifier[] - */ - protected $columns = []; - - /** - * Platform specific flags. - * array($flagName => true) * - * @var true[] + * @var array */ - protected $flags = []; + protected array $columns = []; /** - * Platform specific options. + * Platform specific flags * - * @var mixed[] + * @var array */ - private array $options; + protected array $flags = []; /** - * @param string[] $columns - * @param string[] $flags - * @param mixed[] $options + * @param array $columns + * @param array $flags + * @param array $options */ - public function __construct(string $name, array $columns, array $flags = [], array $options = []) - { + public function __construct( + string $name, + array $columns, + array $flags = [], + private readonly array $options = [], + ) { $this->_setName($name); - $this->options = $options; - foreach ($columns as $column) { $this->addColumn($column); } @@ -57,17 +52,27 @@ public function __construct(string $name, array $columns, array $flags = [], arr } /** - * {@inheritDoc} + * Returns the names of the referencing table columns the constraint is associated with. + * + * @return list */ - public function getColumns() + public function getColumns(): array { return array_keys($this->columns); } /** - * {@inheritDoc} + * Returns the quoted representation of the column names the constraint is associated with. + * + * But only if they were defined with one or a column name + * is a keyword reserved by the platform. + * Otherwise, the plain unquoted value as inserted is returned. + * + * @param AbstractPlatform $platform The platform to use for quotation. + * + * @return list */ - public function getQuotedColumns(AbstractPlatform $platform) + public function getQuotedColumns(AbstractPlatform $platform): array { $columns = []; @@ -78,16 +83,16 @@ public function getQuotedColumns(AbstractPlatform $platform) return $columns; } - /** @return string[] */ + /** @return array */ public function getUnquotedColumns(): array { - return array_map([$this, 'trimQuotes'], $this->getColumns()); + return array_map($this->trimQuotes(...), $this->getColumns()); } /** * Returns platform specific flags for unique constraint. * - * @return string[] + * @return array */ public function getFlags(): array { @@ -101,7 +106,7 @@ public function getFlags(): array * * @example $uniqueConstraint->addFlag('CLUSTERED') */ - public function addFlag(string $flag): UniqueConstraint + public function addFlag(string $flag): self { $this->flags[strtolower($flag)] = true; @@ -124,29 +129,22 @@ public function removeFlag(string $flag): void unset($this->flags[strtolower($flag)]); } - /** - * Does this unique constraint have a specific option? - */ public function hasOption(string $name): bool { return isset($this->options[strtolower($name)]); } - /** @return mixed */ - public function getOption(string $name) + public function getOption(string $name): mixed { return $this->options[strtolower($name)]; } - /** @return mixed[] */ + /** @return array */ public function getOptions(): array { return $this->options; } - /** - * Adds a new column to the unique constraint. - */ protected function addColumn(string $column): void { $this->columns[$column] = new Identifier($column); diff --git a/doctrine/dbal/src/Schema/View.php b/doctrine/dbal/src/Schema/View.php index b19f2ad1f..81f5f8a7d 100644 --- a/doctrine/dbal/src/Schema/View.php +++ b/doctrine/dbal/src/Schema/View.php @@ -1,5 +1,7 @@ _setName($name); - $this->sql = $sql; } - /** @return string */ - public function getSql() + public function getSql(): string { return $this->sql; } diff --git a/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php b/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php deleted file mode 100644 index f8f3b5825..000000000 --- a/doctrine/dbal/src/Schema/Visitor/AbstractVisitor.php +++ /dev/null @@ -1,49 +0,0 @@ -platform = $platform; - } - - /** - * {@inheritDoc} - */ - public function acceptNamespace($namespaceName) - { - if (! $this->platform->supportsSchemas()) { - return; - } - - $this->createNamespaceQueries[] = $this->platform->getCreateSchemaSQL($namespaceName); - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - $this->createTableQueries = array_merge($this->createTableQueries, $this->platform->getCreateTableSQL($table)); - } - - /** - * {@inheritDoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - if (! $this->platform->supportsForeignKeyConstraints()) { - return; - } - - $this->createFkConstraintQueries[] = $this->platform->getCreateForeignKeySQL($fkConstraint, $localTable); - } - - /** - * {@inheritDoc} - */ - public function acceptSequence(Sequence $sequence) - { - $this->createSequenceQueries[] = $this->platform->getCreateSequenceSQL($sequence); - } - - /** @return void */ - public function resetQueries() - { - $this->createNamespaceQueries = []; - $this->createTableQueries = []; - $this->createSequenceQueries = []; - $this->createFkConstraintQueries = []; - } - - /** - * Gets all queries collected so far. - * - * @return string[] - */ - public function getQueries() - { - return array_merge( - $this->createNamespaceQueries, - $this->createSequenceQueries, - $this->createTableQueries, - $this->createFkConstraintQueries, - ); - } -} diff --git a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php b/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php deleted file mode 100644 index ddec6b4ae..000000000 --- a/doctrine/dbal/src/Schema/Visitor/DropSchemaSqlCollector.php +++ /dev/null @@ -1,107 +0,0 @@ -platform = $platform; - $this->initializeQueries(); - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - $this->tables->attach($table); - } - - /** - * {@inheritDoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - if (strlen($fkConstraint->getName()) === 0) { - throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); - } - - $this->constraints->attach($fkConstraint, $localTable); - } - - /** - * {@inheritDoc} - */ - public function acceptSequence(Sequence $sequence) - { - $this->sequences->attach($sequence); - } - - /** @return void */ - public function clearQueries() - { - $this->initializeQueries(); - } - - /** @return string[] */ - public function getQueries() - { - $sql = []; - - foreach ($this->constraints as $fkConstraint) { - assert($fkConstraint instanceof ForeignKeyConstraint); - $localTable = $this->constraints[$fkConstraint]; - $sql[] = $this->platform->getDropForeignKeySQL( - $fkConstraint->getQuotedName($this->platform), - $localTable->getQuotedName($this->platform), - ); - } - - foreach ($this->sequences as $sequence) { - assert($sequence instanceof Sequence); - $sql[] = $this->platform->getDropSequenceSQL($sequence->getQuotedName($this->platform)); - } - - foreach ($this->tables as $table) { - assert($table instanceof Table); - $sql[] = $this->platform->getDropTableSQL($table->getQuotedName($this->platform)); - } - - return $sql; - } - - private function initializeQueries(): void - { - $this->constraints = new SplObjectStorage(); - $this->sequences = new SplObjectStorage(); - $this->tables = new SplObjectStorage(); - } -} diff --git a/doctrine/dbal/src/Schema/Visitor/Graphviz.php b/doctrine/dbal/src/Schema/Visitor/Graphviz.php deleted file mode 100644 index 5eff0d945..000000000 --- a/doctrine/dbal/src/Schema/Visitor/Graphviz.php +++ /dev/null @@ -1,164 +0,0 @@ -output .= $this->createNodeRelation( - $fkConstraint->getLocalTableName() . ':col' . current($fkConstraint->getLocalColumns()) . ':se', - $fkConstraint->getForeignTableName() . ':col' . current($fkConstraint->getForeignColumns()) . ':se', - [ - 'dir' => 'back', - 'arrowtail' => 'dot', - 'arrowhead' => 'normal', - ], - ); - } - - /** - * {@inheritDoc} - */ - public function acceptSchema(Schema $schema) - { - $this->output = 'digraph "' . $schema->getName() . '" {' . "\n"; - $this->output .= 'splines = true;' . "\n"; - $this->output .= 'overlap = false;' . "\n"; - $this->output .= 'outputorder=edgesfirst;' . "\n"; - $this->output .= 'mindist = 0.6;' . "\n"; - $this->output .= 'sep = .2;' . "\n"; - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - $this->output .= $this->createNode( - $table->getName(), - [ - 'label' => $this->createTableLabel($table), - 'shape' => 'plaintext', - ], - ); - } - - private function createTableLabel(Table $table): string - { - // Start the table - $label = '<
'; - - // The title - $label .= ''; - - // The attributes block - foreach ($table->getColumns() as $column) { - $columnLabel = $column->getName(); - - $label .= '' - . '' - . '' - . ''; - } - - // End the table - $label .= '
' - . '' . $table->getName() . '
' - . '' . $columnLabel . '' - . '' - . '' - . strtolower($column->getType()->getName()) - . '' - . ''; - - $primaryKey = $table->getPrimaryKey(); - - if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns(), true)) { - $label .= "\xe2\x9c\xb7"; - } - - $label .= '
>'; - - return $label; - } - - /** - * @param string $name - * @param string[] $options - */ - private function createNode($name, $options): string - { - $node = $name . ' ['; - foreach ($options as $key => $value) { - $node .= $key . '=' . $value . ' '; - } - - $node .= "]\n"; - - return $node; - } - - /** - * @param string $node1 - * @param string $node2 - * @param string[] $options - */ - private function createNodeRelation($node1, $node2, $options): string - { - $relation = $node1 . ' -> ' . $node2 . ' ['; - foreach ($options as $key => $value) { - $relation .= $key . '=' . $value . ' '; - } - - $relation .= "]\n"; - - return $relation; - } - - /** - * Get Graphviz Output - * - * @return string - */ - public function getOutput() - { - return $this->output . '}'; - } - - /** - * Writes dot language output to a file. This should usually be a *.dot file. - * - * You have to convert the output into a viewable format. For example use "neato" on linux systems - * and execute: - * - * neato -Tpng -o er.png er.dot - * - * @param string $filename - * - * @return void - */ - public function write($filename) - { - file_put_contents($filename, $this->getOutput()); - } -} diff --git a/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php b/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php deleted file mode 100644 index 443824342..000000000 --- a/doctrine/dbal/src/Schema/Visitor/NamespaceVisitor.php +++ /dev/null @@ -1,20 +0,0 @@ -schema = $schema; - } - - /** - * {@inheritDoc} - */ - public function acceptTable(Table $table) - { - if ($this->schema === null) { - return; - } - - if ($table->isInDefaultNamespace($this->schema->getName())) { - return; - } - - $this->schema->dropTable($table->getName()); - } - - /** - * {@inheritDoc} - */ - public function acceptSequence(Sequence $sequence) - { - if ($this->schema === null) { - return; - } - - if ($sequence->isInDefaultNamespace($this->schema->getName())) { - return; - } - - $this->schema->dropSequence($sequence->getName()); - } - - /** - * {@inheritDoc} - */ - public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) - { - if ($this->schema === null) { - return; - } - - // The table may already be deleted in a previous - // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that - // point to nowhere. - if (! $this->schema->hasTable($fkConstraint->getForeignTableName())) { - $localTable->removeForeignKey($fkConstraint->getName()); - - return; - } - - $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); - if ($foreignTable->isInDefaultNamespace($this->schema->getName())) { - return; - } - - $localTable->removeForeignKey($fkConstraint->getName()); - } -} diff --git a/doctrine/dbal/src/Schema/Visitor/Visitor.php b/doctrine/dbal/src/Schema/Visitor/Visitor.php deleted file mode 100644 index 8b34864c1..000000000 --- a/doctrine/dbal/src/Schema/Visitor/Visitor.php +++ /dev/null @@ -1,45 +0,0 @@ -Statement for the given SQL and Connection. * * @internal The statement can be only instantiated by {@see Connection}. * - * @param Connection $conn The connection for handling statement errors. - * @param Driver\Statement $statement The underlying driver-level statement. - * @param string $sql The SQL of the statement. + * @param Connection $conn The connection for handling statement errors. + * @param Driver\Statement $stmt The underlying driver-level statement. + * @param string $sql The SQL of the statement. * * @throws Exception */ - public function __construct(Connection $conn, Driver\Statement $statement, string $sql) - { - $this->conn = $conn; - $this->stmt = $statement; - $this->sql = $sql; + public function __construct( + protected Connection $conn, + protected Driver\Statement $stmt, + protected string $sql, + ) { $this->platform = $conn->getDatabasePlatform(); } @@ -83,178 +60,83 @@ public function __construct(Connection $conn, Driver\Statement $statement, strin * type and the value undergoes the conversion routines of the mapping type before * being bound. * - * @param string|int $param The name or position of the parameter. - * @param mixed $value The value of the parameter. - * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. - * - * @return bool TRUE on success, FALSE on failure. + * @param string|int $param Parameter identifier. For a prepared statement using named placeholders, + * this will be a parameter name of the form :name. For a prepared statement + * using question mark placeholders, this will be the 1-indexed position + * of the parameter. + * @param mixed $value The value to bind to the parameter. + * @param ParameterType|string|Type $type Either a {@see \Doctrine\DBAL\ParameterType} or a DBAL mapping type name + * or instance. * * @throws Exception */ - public function bindValue($param, $value, $type = ParameterType::STRING) - { + public function bindValue( + string|int $param, + mixed $value, + string|ParameterType|Type $type = ParameterType::STRING, + ): void { $this->params[$param] = $value; $this->types[$param] = $type; - $bindingType = ParameterType::STRING; - - if ($type !== null) { - if (is_string($type)) { - $type = Type::getType($type); - } + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + $bindingType = $type->getBindingType(); + } else { $bindingType = $type; - - if ($type instanceof Type) { - $value = $type->convertToDatabaseValue($value, $this->platform); - $bindingType = $type->getBindingType(); - } } try { - return $this->stmt->bindValue($param, $value, $bindingType); + $this->stmt->bindValue($param, $value, $bindingType); } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } - /** - * Binds a parameter to a value by reference. - * - * Binding a parameter by reference does not support DBAL mapping types. - * - * @deprecated Use {@see bindValue()} instead. - * - * @param string|int $param The name or position of the parameter. - * @param mixed $variable The reference to the variable to bind. - * @param int $type The binding type. - * @param int|null $length Must be specified when using an OUT bind - * so that PHP allocates enough memory to hold the returned value. - * - * @return bool TRUE on success, FALSE on failure. - * - * @throws Exception - */ - public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + /** @throws Exception */ + private function execute(): Result { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5563', - '%s is deprecated. Use bindValue() instead.', - __METHOD__, - ); - - $this->params[$param] = $variable; - $this->types[$param] = $type; - - try { - if (func_num_args() > 3) { - return $this->stmt->bindParam($param, $variable, $type, $length); - } - - return $this->stmt->bindParam($param, $variable, $type); - } catch (Driver\Exception $e) { - throw $this->conn->convertException($e); - } - } - - /** - * Executes the statement with the currently bound parameters. - * - * @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead - * - * @param mixed[]|null $params - * - * @throws Exception - */ - public function execute($params = null): Result - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/4580', - '%s() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead', - __METHOD__, - ); - - if ($params !== null) { - $this->params = $params; - } - - $logger = $this->conn->getConfiguration()->getSQLLogger(); - if ($logger !== null) { - $logger->startQuery($this->sql, $this->params, $this->types); - } - try { return new Result( - $this->stmt->execute($params), + $this->stmt->execute(), $this->conn, ); } catch (Driver\Exception $ex) { throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); - } finally { - if ($logger !== null) { - $logger->stopQuery(); - } } } /** * Executes the statement with the currently bound parameters and return result. * - * @param mixed[] $params - * * @throws Exception */ - public function executeQuery(array $params = []): Result + public function executeQuery(): Result { - if (func_num_args() > 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::executeQuery() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - } - - if ($params === []) { - $params = null; // Workaround as long execute() exists and used internally. - } - - return $this->execute($params); + return $this->execute(); } /** * Executes the statement with the currently bound parameters and return affected rows. * - * @param mixed[] $params + * If the number of rows exceeds {@see PHP_INT_MAX}, it might be returned as string if the driver supports it. + * + * @return int|numeric-string * * @throws Exception */ - public function executeStatement(array $params = []): int + public function executeStatement(): int|string { - if (func_num_args() > 0) { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5556', - 'Passing $params to Statement::executeStatement() is deprecated. Bind parameters using' - . ' Statement::bindParam() or Statement::bindValue() instead.', - ); - } - - if ($params === []) { - $params = null; // Workaround as long execute() exists and used internally. - } - - return $this->execute($params)->rowCount(); + return $this->execute()->rowCount(); } /** * Gets the wrapped driver statement. - * - * @return Driver\Statement */ - public function getWrappedStatement() + public function getWrappedStatement(): Driver\Statement { return $this->stmt; } diff --git a/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php b/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php deleted file mode 100644 index 562b5ce45..000000000 --- a/doctrine/dbal/src/Tools/Console/Command/CommandCompatibility.php +++ /dev/null @@ -1,35 +0,0 @@ -hasReturnType()) { - /** @internal */ - trait CommandCompatibility - { - protected function execute(InputInterface $input, OutputInterface $output): int - { - return $this->doExecute($input, $output); - } - } -} else { - /** @internal */ - trait CommandCompatibility - { - /** - * {@inheritDoc} - * - * @return int - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - return $this->doExecute($input, $output); - } - } -} diff --git a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php b/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php deleted file mode 100644 index e63315466..000000000 --- a/doctrine/dbal/src/Tools/Console/Command/ReservedWordsCommand.php +++ /dev/null @@ -1,216 +0,0 @@ - */ - private array $keywordLists; - - private ConnectionProvider $connectionProvider; - - public function __construct(ConnectionProvider $connectionProvider) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5431', - 'ReservedWordsCommand is deprecated. Use database documentation instead.', - ); - - parent::__construct(); - - $this->connectionProvider = $connectionProvider; - - $this->keywordLists = [ - 'db2' => new DB2Keywords(), - 'mariadb102' => new MariaDb102Keywords(), - 'mysql' => new MySQLKeywords(), - 'mysql57' => new MySQL57Keywords(), - 'mysql80' => new MySQL80Keywords(), - 'oracle' => new OracleKeywords(), - 'pgsql' => new PostgreSQL94Keywords(), - 'pgsql100' => new PostgreSQL100Keywords(), - 'sqlite' => new SQLiteKeywords(), - 'sqlserver' => new SQLServer2012Keywords(), - ]; - } - - /** - * Add or replace a keyword list. - */ - public function setKeywordList(string $name, KeywordList $keywordList): void - { - $this->keywordLists[$name] = $keywordList; - } - - /** - * If you want to add or replace a keywords list use this command. - * - * @param string $name - * @param class-string $class - * - * @return void - */ - public function setKeywordListClass($name, $class) - { - Deprecation::trigger( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/issues/4510', - 'ReservedWordsCommand::setKeywordListClass() is deprecated,' - . ' use ReservedWordsCommand::setKeywordList() instead.', - ); - - $this->keywordLists[$name] = new $class(); - } - - /** @return void */ - protected function configure() - { - $this - ->setName('dbal:reserved-words') - ->setDescription('Checks if the current database contains identifiers that are reserved.') - ->setDefinition([ - new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), - new InputOption( - 'list', - 'l', - InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, - 'Keyword-List name.', - ), - ]) - ->setHelp(<<<'EOT' -Checks if the current database contains tables and columns -with names that are identifiers in this dialect or in other SQL dialects. - -By default all supported platform keywords are checked: - - %command.full_name% - -If you want to check against specific dialects you can -pass them to the command: - - %command.full_name% -l mysql -l pgsql - -The following keyword lists are currently shipped with Doctrine: - - * db2 - * mariadb102 - * mysql - * mysql57 - * mysql80 - * oracle - * pgsql - * pgsql100 - * sqlite - * sqlserver -EOT); - } - - /** @throws Exception */ - private function doExecute(InputInterface $input, OutputInterface $output): int - { - $output->writeln( - 'The dbal:reserved-words command is deprecated.' - . ' Use the documentation on the used database platform(s) instead.', - ); - $output->writeln(''); - - $conn = $this->getConnection($input); - - $keywordLists = $input->getOption('list'); - - if (is_string($keywordLists)) { - $keywordLists = [$keywordLists]; - } elseif (! is_array($keywordLists)) { - $keywordLists = []; - } - - if (count($keywordLists) === 0) { - $keywordLists = array_keys($this->keywordLists); - } - - $keywords = []; - foreach ($keywordLists as $keywordList) { - if (! isset($this->keywordLists[$keywordList])) { - throw new InvalidArgumentException( - "There exists no keyword list with name '" . $keywordList . "'. " . - 'Known lists: ' . implode(', ', array_keys($this->keywordLists)), - ); - } - - $keywords[] = $this->keywordLists[$keywordList]; - } - - $output->write( - 'Checking keyword violations for ' . implode(', ', $keywordLists) . '...', - true, - ); - - $schema = $conn->getSchemaManager()->introspectSchema(); - $visitor = new ReservedKeywordsValidator($keywords); - $schema->visit($visitor); - - $violations = $visitor->getViolations(); - if (count($violations) !== 0) { - $output->write( - 'There are ' . count($violations) . ' reserved keyword violations' - . ' in your database schema:', - true, - ); - - foreach ($violations as $violation) { - $output->write(' - ' . $violation, true); - } - - return 1; - } - - $output->write('No reserved keywords violations have been found!', true); - - return 0; - } - - private function getConnection(InputInterface $input): Connection - { - $connectionName = $input->getOption('connection'); - assert(is_string($connectionName) || $connectionName === null); - - if ($connectionName !== null) { - return $this->connectionProvider->getConnection($connectionName); - } - - return $this->connectionProvider->getDefaultConnection(); - } -} diff --git a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php index 4e5681e27..8ec6f238d 100644 --- a/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php +++ b/doctrine/dbal/src/Tools/Console/Command/RunSqlCommand.php @@ -1,5 +1,7 @@ connectionProvider = $connectionProvider; } - /** @return void */ - protected function configure() + protected function configure(): void { $this ->setName('dbal:run-sql') @@ -57,8 +52,12 @@ protected function configure() EOT); } - /** @throws Exception */ - private function doExecute(InputInterface $input, OutputInterface $output): int + /** + * {@inheritDoc} + * + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { $conn = $this->getConnection($input); $io = new SymfonyStyle($input, $output); @@ -66,7 +65,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int $sql = $input->getArgument('sql'); if ($sql === null) { - throw new RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); + throw new RuntimeException('Argument "sql" is required in order to execute this command correctly.'); } assert(is_string($sql)); diff --git a/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php b/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php index 81ca4182a..049d658a6 100644 --- a/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php +++ b/doctrine/dbal/src/Tools/Console/ConnectionNotFound.php @@ -1,5 +1,7 @@ connection = $connection; - $this->defaultConnectionName = $defaultConnectionName; + public function __construct( + private readonly Connection $connection, + private readonly string $defaultConnectionName = 'default', + ) { } public function getDefaultConnection(): Connection diff --git a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php b/doctrine/dbal/src/Tools/Console/ConsoleRunner.php deleted file mode 100644 index e8fa3c60b..000000000 --- a/doctrine/dbal/src/Tools/Console/ConsoleRunner.php +++ /dev/null @@ -1,81 +0,0 @@ -setCatchExceptions(true); - self::addCommands($cli, $connectionProvider); - $cli->addCommands($commands); - $cli->run(); - } - - /** @return void */ - public static function addCommands(Application $cli, ConnectionProvider $connectionProvider) - { - $cli->addCommands([ - new RunSqlCommand($connectionProvider), - new ReservedWordsCommand($connectionProvider), - ]); - } - - /** - * Prints the instructions to create a configuration file - * - * @deprecated This method will be removed without replacement. - * - * @return void - */ - public static function printCliConfigTemplate() - { - echo <<<'HELP' -You are missing a "cli-config.php" or "config/cli-config.php" file in your -project, which is required to get the Doctrine-DBAL Console working. You can use the -following sample as a template: - -> */ - private array $schemeMapping; - /** @param array> $schemeMapping An array used to map DSN schemes to DBAL drivers */ - public function __construct(array $schemeMapping = []) - { - $this->schemeMapping = $schemeMapping; + public function __construct( + private readonly array $schemeMapping = [], + ) { } /** @@ -38,7 +37,7 @@ public function __construct(array $schemeMapping = []) */ public function parse( #[SensitiveParameter] - string $dsn + string $dsn, ): array { // (pdo-)?sqlite3?:///... => (pdo-)?sqlite3?://localhost/... or else the URL will be invalid $url = preg_replace('#^((?:pdo-)?sqlite3?):///#', '$1://localhost/', $dsn); diff --git a/doctrine/dbal/src/TransactionIsolationLevel.php b/doctrine/dbal/src/TransactionIsolationLevel.php index 9020343ab..2b094dbe8 100644 --- a/doctrine/dbal/src/TransactionIsolationLevel.php +++ b/doctrine/dbal/src/TransactionIsolationLevel.php @@ -4,30 +4,10 @@ namespace Doctrine\DBAL; -final class TransactionIsolationLevel +enum TransactionIsolationLevel { - /** - * Transaction isolation level READ UNCOMMITTED. - */ - public const READ_UNCOMMITTED = 1; - - /** - * Transaction isolation level READ COMMITTED. - */ - public const READ_COMMITTED = 2; - - /** - * Transaction isolation level REPEATABLE READ. - */ - public const REPEATABLE_READ = 3; - - /** - * Transaction isolation level SERIALIZABLE. - */ - public const SERIALIZABLE = 4; - - /** @codeCoverageIgnore */ - private function __construct() - { - } + case READ_UNCOMMITTED; + case READ_COMMITTED; + case REPEATABLE_READ; + case SERIALIZABLE; } diff --git a/doctrine/dbal/src/Types/ArrayType.php b/doctrine/dbal/src/Types/ArrayType.php deleted file mode 100644 index c2aa2f4de..000000000 --- a/doctrine/dbal/src/Types/ArrayType.php +++ /dev/null @@ -1,92 +0,0 @@ -getClobTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - // @todo 3.0 - $value === null check to save real NULL in database - return serialize($value); - } - - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) - { - if ($value === null) { - return null; - } - - $value = is_resource($value) ? stream_get_contents($value) : $value; - - set_error_handler(function (int $code, string $message): bool { - if ($code === E_DEPRECATED || $code === E_USER_DEPRECATED) { - return false; - } - - throw ConversionException::conversionFailedUnserialization($this->getName(), $message); - }); - - try { - return unserialize($value); - } finally { - restore_error_handler(); - } - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::ARRAY; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } -} diff --git a/doctrine/dbal/src/Types/AsciiStringType.php b/doctrine/dbal/src/Types/AsciiStringType.php index 4ea92d974..cd928eee3 100644 --- a/doctrine/dbal/src/Types/AsciiStringType.php +++ b/doctrine/dbal/src/Types/AsciiStringType.php @@ -17,13 +17,8 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st return $platform->getAsciiStringTypeDeclarationSQL($column); } - public function getBindingType(): int + public function getBindingType(): ParameterType { return ParameterType::ASCII; } - - public function getName(): string - { - return Types::ASCII_STRING; - } } diff --git a/doctrine/dbal/src/Types/BigIntType.php b/doctrine/dbal/src/Types/BigIntType.php index 8d57a1121..0cb14c5b4 100644 --- a/doctrine/dbal/src/Types/BigIntType.php +++ b/doctrine/dbal/src/Types/BigIntType.php @@ -1,50 +1,61 @@ getBigIntTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::STRING; } /** - * {@inheritDoc} - * * @param T $value * - * @return (T is null ? null : string) + * @return (T is null ? null : int|string) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): int|string|null { - return $value === null ? null : (string) $value; + if ($value === null || is_int($value)) { + return $value; + } + + if ($value > PHP_INT_MIN && $value < PHP_INT_MAX) { + return (int) $value; + } + + assert( + is_string($value), + 'DBAL assumes values outside of the integer range to be returned as string by the database driver.', + ); + + return $value; } } diff --git a/doctrine/dbal/src/Types/BinaryType.php b/doctrine/dbal/src/Types/BinaryType.php index acbbd87ad..d400dd5bf 100644 --- a/doctrine/dbal/src/Types/BinaryType.php +++ b/doctrine/dbal/src/Types/BinaryType.php @@ -1,16 +1,16 @@ getBinaryTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return null; } - if (is_string($value)) { - $fp = fopen('php://temp', 'rb+'); - assert(is_resource($fp)); - fwrite($fp, $value); - fseek($fp, 0); - $value = $fp; + if (is_resource($value)) { + $value = stream_get_contents($value); } - if (! is_resource($value)) { - throw ConversionException::conversionFailed($value, Types::BINARY); + if (! is_string($value)) { + throw ValueNotConvertible::new($value, Types::BINARY); } return $value; } - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::BINARY; - } - - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::BINARY; } diff --git a/doctrine/dbal/src/Types/BlobType.php b/doctrine/dbal/src/Types/BlobType.php index cfaabec90..c5415bcf1 100644 --- a/doctrine/dbal/src/Types/BlobType.php +++ b/doctrine/dbal/src/Types/BlobType.php @@ -1,9 +1,12 @@ getBlobTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed { if ($value === null) { return null; @@ -43,24 +43,13 @@ public function convertToPHPValue($value, AbstractPlatform $platform) } if (! is_resource($value)) { - throw ConversionException::conversionFailed($value, Types::BLOB); + throw ValueNotConvertible::new($value, Types::BLOB); } return $value; } - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::BLOB; - } - - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::LARGE_OBJECT; } diff --git a/doctrine/dbal/src/Types/BooleanType.php b/doctrine/dbal/src/Types/BooleanType.php index 7dc7f3a9d..e837e586e 100644 --- a/doctrine/dbal/src/Types/BooleanType.php +++ b/doctrine/dbal/src/Types/BooleanType.php @@ -1,11 +1,11 @@ getBooleanTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed { return $platform->convertBooleansToDatabaseValue($value); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : bool) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?bool { return $platform->convertFromBoolean($value); } - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::BOOLEAN; - } - - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::BOOLEAN; } - - /** - * @deprecated - * - * @return bool - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - // We require a commented boolean type in order to distinguish between - // boolean and smallint as both (have to) map to the same native type. - return $platform instanceof DB2Platform; - } } diff --git a/doctrine/dbal/src/Types/ConversionException.php b/doctrine/dbal/src/Types/ConversionException.php index 154b06d3e..b34c77c27 100644 --- a/doctrine/dbal/src/Types/ConversionException.php +++ b/doctrine/dbal/src/Types/ConversionException.php @@ -1,123 +1,16 @@ 32 ? substr($value, 0, 20) . '...' : $value; - - return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType, 0, $previous); - } - - /** - * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement - * about the expected format. - * - * @param mixed $value - * @param string $toType - * @param string $expectedFormat - * - * @return ConversionException - */ - public static function conversionFailedFormat($value, $toType, $expectedFormat, ?Throwable $previous = null) - { - $value = strlen($value) > 32 ? substr($value, 0, 20) . '...' : $value; - - return new self( - 'Could not convert database value "' . $value . '" to Doctrine Type ' . - $toType . '. Expected format: ' . $expectedFormat, - 0, - $previous, - ); - } - - /** - * Thrown when the PHP value passed to the converter was not of the expected type. - * - * @param mixed $value - * @param string $toType - * @param string[] $possibleTypes - * - * @return ConversionException - */ - public static function conversionFailedInvalidType( - $value, - $toType, - array $possibleTypes, - ?Throwable $previous = null - ) { - if (is_scalar($value) || $value === null) { - return new self(sprintf( - 'Could not convert PHP value %s to type %s. Expected one of the following types: %s', - var_export($value, true), - $toType, - implode(', ', $possibleTypes), - ), 0, $previous); - } - - return new self(sprintf( - 'Could not convert PHP value of type %s to type %s. Expected one of the following types: %s', - is_object($value) ? get_class($value) : gettype($value), - $toType, - implode(', ', $possibleTypes), - ), 0, $previous); - } - - /** - * @param mixed $value - * @param string $format - * @param string $error - * - * @return ConversionException - */ - public static function conversionFailedSerialization($value, $format, $error /*, ?Throwable $previous = null */) - { - $actualType = is_object($value) ? get_class($value) : gettype($value); - - return new self(sprintf( - "Could not convert PHP type '%s' to '%s', as an '%s' error was triggered by the serialization", - $actualType, - $format, - $error, - ), 0, func_num_args() >= 4 ? func_get_arg(3) : null); - } - - public static function conversionFailedUnserialization(string $format, string $error): self - { - return new self(sprintf( - "Could not convert database value to '%s' as an error was triggered by the unserialization: '%s'", - $format, - $error, - )); - } } diff --git a/doctrine/dbal/src/Types/DateImmutableType.php b/doctrine/dbal/src/Types/DateImmutableType.php index da96b69d5..732efcd1a 100644 --- a/doctrine/dbal/src/Types/DateImmutableType.php +++ b/doctrine/dbal/src/Types/DateImmutableType.php @@ -1,34 +1,35 @@ getDateTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; @@ -38,23 +39,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format($platform->getDateFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTimeImmutable::class], ); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable { if ($value === null || $value instanceof DateTimeImmutable) { return $value; @@ -63,30 +62,13 @@ public function convertToPHPValue($value, AbstractPlatform $platform) $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getDateFormatString(), $value); if ($dateTime === false) { - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateFormatString(), ); } return $dateTime; } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/DateIntervalType.php b/doctrine/dbal/src/Types/DateIntervalType.php index 1630dc556..29842ae54 100644 --- a/doctrine/dbal/src/Types/DateIntervalType.php +++ b/doctrine/dbal/src/Types/DateIntervalType.php @@ -1,10 +1,13 @@ format(self::FORMAT); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateInterval::class]); + throw InvalidType::new($value, static::class, ['null', DateInterval::class]); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateInterval) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateInterval { if ($value === null || $value instanceof DateInterval) { return $value; @@ -87,24 +78,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $interval; } catch (Throwable $exception) { - throw ConversionException::conversionFailedFormat($value, $this->getName(), self::FORMAT, $exception); + throw InvalidFormat::new($value, static::class, self::FORMAT, $exception); } } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/DateTimeImmutableType.php b/doctrine/dbal/src/Types/DateTimeImmutableType.php index a8c7fec96..2d49d1dd7 100644 --- a/doctrine/dbal/src/Types/DateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/DateTimeImmutableType.php @@ -1,35 +1,36 @@ getDateTimeTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; @@ -39,23 +40,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format($platform->getDateTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTimeImmutable::class], ); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable { if ($value === null || $value instanceof DateTimeImmutable) { return $value; @@ -70,29 +69,12 @@ public function convertToPHPValue($value, AbstractPlatform $platform) try { return new DateTimeImmutable($value); } catch (Exception $e) { - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateTimeFormatString(), $e, ); } } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/DateTimeType.php b/doctrine/dbal/src/Types/DateTimeType.php index 3ff592ae1..9fd0ba028 100644 --- a/doctrine/dbal/src/Types/DateTimeType.php +++ b/doctrine/dbal/src/Types/DateTimeType.php @@ -1,16 +1,15 @@ getDateTimeTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateTimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getDateTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), - ['null', DateTime::class, DateTimeImmutable::class], + static::class, + ['null', DateTime::class], ); } /** - * {@inheritDoc} - * * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateTimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } @@ -104,9 +69,9 @@ public function convertToPHPValue($value, AbstractPlatform $platform) try { return new DateTime($value); } catch (Exception $e) { - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateTimeFormatString(), $e, ); diff --git a/doctrine/dbal/src/Types/DateTimeTzImmutableType.php b/doctrine/dbal/src/Types/DateTimeTzImmutableType.php index e700f68d4..a7d662dca 100644 --- a/doctrine/dbal/src/Types/DateTimeTzImmutableType.php +++ b/doctrine/dbal/src/Types/DateTimeTzImmutableType.php @@ -1,34 +1,35 @@ getDateTimeTzTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @psalm-param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; @@ -38,23 +39,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format($platform->getDateTimeTzFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTimeImmutable::class], ); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable { if ($value === null || $value instanceof DateTimeImmutable) { return $value; @@ -66,27 +65,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $dateTime; } - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateTimeTzFormatString(), ); } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/DateTimeTzType.php b/doctrine/dbal/src/Types/DateTimeTzType.php index 1980fd334..98e65696e 100644 --- a/doctrine/dbal/src/Types/DateTimeTzType.php +++ b/doctrine/dbal/src/Types/DateTimeTzType.php @@ -1,28 +1,28 @@ getDateTimeTzTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateTimeTzImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getDateTimeTzFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTime::class], ); } /** - * {@inheritDoc} - * * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateTimeTzImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } @@ -112,9 +78,9 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $dateTime; } - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateTimeTzFormatString(), ); } diff --git a/doctrine/dbal/src/Types/DateType.php b/doctrine/dbal/src/Types/DateType.php index 842e8bd09..6548c53ba 100644 --- a/doctrine/dbal/src/Types/DateType.php +++ b/doctrine/dbal/src/Types/DateType.php @@ -1,92 +1,57 @@ getDateTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @psalm-param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed { if ($value === null) { return $value; } - if ($value instanceof DateTimeInterface) { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateImmutableType::class, - __FUNCTION__, - ); - } - + if ($value instanceof DateTime) { return $value->format($platform->getDateFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); + throw InvalidType::new($value, static::class, ['null', DateTime::class]); } /** - * {@inheritDoc} - * * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - DateImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } @@ -95,9 +60,9 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $dateTime; } - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getDateFormatString(), ); } diff --git a/doctrine/dbal/src/Types/DecimalType.php b/doctrine/dbal/src/Types/DecimalType.php index 308134b08..7301a3383 100644 --- a/doctrine/dbal/src/Types/DecimalType.php +++ b/doctrine/dbal/src/Types/DecimalType.php @@ -1,15 +1,14 @@ getDecimalTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?string { - // Some drivers starting from PHP 8.1 can represent decimals as float/int + // Some drivers can represent decimals as float/int // See also: https://github.com/doctrine/dbal/pull/4818 - if ((PHP_VERSION_ID >= 80100 || $platform instanceof SqlitePlatform) && (is_float($value) || is_int($value))) { + if (is_float($value) || is_int($value)) { return (string) $value; } diff --git a/doctrine/dbal/src/Types/Exception/InvalidFormat.php b/doctrine/dbal/src/Types/Exception/InvalidFormat.php new file mode 100644 index 000000000..e7cd63953 --- /dev/null +++ b/doctrine/dbal/src/Types/Exception/InvalidFormat.php @@ -0,0 +1,39 @@ + 32 ? substr($value, 0, 20) . '...' : $value, + $toType, + $expectedFormat ?? '', + ), + 0, + $previous, + ); + } +} diff --git a/doctrine/dbal/src/Types/Exception/InvalidType.php b/doctrine/dbal/src/Types/Exception/InvalidType.php new file mode 100644 index 000000000..56aa877cb --- /dev/null +++ b/doctrine/dbal/src/Types/Exception/InvalidType.php @@ -0,0 +1,53 @@ + 32 ? substr($value, 0, 20) . '...' : $value, + $toType, + ); + } + + return new self($message, 0, $previous); + } +} diff --git a/doctrine/dbal/src/Types/FloatType.php b/doctrine/dbal/src/Types/FloatType.php index e01b77413..40e11f9f5 100644 --- a/doctrine/dbal/src/Types/FloatType.php +++ b/doctrine/dbal/src/Types/FloatType.php @@ -1,5 +1,7 @@ getFloatDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : float) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?float { return $value === null ? null : (float) $value; } diff --git a/doctrine/dbal/src/Types/GuidType.php b/doctrine/dbal/src/Types/GuidType.php index 3c8b7f4f8..cc7cc5fc3 100644 --- a/doctrine/dbal/src/Types/GuidType.php +++ b/doctrine/dbal/src/Types/GuidType.php @@ -1,9 +1,10 @@ getGuidTypeDeclarationSQL($column); } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::GUID; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return ! $platform->hasNativeGuidType(); - } } diff --git a/doctrine/dbal/src/Types/IntegerType.php b/doctrine/dbal/src/Types/IntegerType.php index 7c2d7110d..8a711c039 100644 --- a/doctrine/dbal/src/Types/IntegerType.php +++ b/doctrine/dbal/src/Types/IntegerType.php @@ -1,5 +1,7 @@ getIntegerTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : int) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?int { return $value === null ? null : (int) $value; } - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::INTEGER; } diff --git a/doctrine/dbal/src/Types/JsonType.php b/doctrine/dbal/src/Types/JsonType.php index 27f872c88..04c3dce18 100644 --- a/doctrine/dbal/src/Types/JsonType.php +++ b/doctrine/dbal/src/Types/JsonType.php @@ -1,9 +1,12 @@ getJsonTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return null; @@ -45,14 +46,11 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) try { return json_encode($value, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION); } catch (JsonException $e) { - throw ConversionException::conversionFailedSerialization($value, 'json', $e->getMessage(), $e); + throw SerializationFailed::new($value, 'json', $e->getMessage(), $e); } } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed { if ($value === null || $value === '') { return null; @@ -65,32 +63,7 @@ public function convertToPHPValue($value, AbstractPlatform $platform) try { return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { - throw ConversionException::conversionFailed($value, $this->getName(), $e); + throw ValueNotConvertible::new($value, 'json', $e->getMessage(), $e); } } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::JSON; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return ! $platform->hasNativeJsonType(); - } } diff --git a/doctrine/dbal/src/Types/ObjectType.php b/doctrine/dbal/src/Types/ObjectType.php deleted file mode 100644 index 497e9c407..000000000 --- a/doctrine/dbal/src/Types/ObjectType.php +++ /dev/null @@ -1,88 +0,0 @@ -getClobTypeDeclarationSQL($column); - } - - /** - * {@inheritDoc} - * - * @param mixed $value - * - * @return string - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) - { - return serialize($value); - } - - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) - { - if ($value === null) { - return null; - } - - $value = is_resource($value) ? stream_get_contents($value) : $value; - - set_error_handler(function (int $code, string $message): bool { - throw ConversionException::conversionFailedUnserialization($this->getName(), $message); - }); - - try { - return unserialize($value); - } finally { - restore_error_handler(); - } - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::OBJECT; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } -} diff --git a/doctrine/dbal/src/Types/PhpDateMappingType.php b/doctrine/dbal/src/Types/PhpDateMappingType.php new file mode 100644 index 000000000..200f2772c --- /dev/null +++ b/doctrine/dbal/src/Types/PhpDateMappingType.php @@ -0,0 +1,14 @@ +getClobTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - * - * @param mixed $value - * - * @return string|null - */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if (! is_array($value) || count($value) === 0) { return null; @@ -43,14 +37,8 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return implode(',', $value); } - /** - * {@inheritDoc} - * - * @param mixed $value - * - * @return list - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + /** @return list */ + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): array { if ($value === null) { return []; @@ -60,29 +48,4 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return explode(',', $value); } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::SIMPLE_ARRAY; - } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/SmallIntType.php b/doctrine/dbal/src/Types/SmallIntType.php index 2c8567a11..ba0899c28 100644 --- a/doctrine/dbal/src/Types/SmallIntType.php +++ b/doctrine/dbal/src/Types/SmallIntType.php @@ -1,5 +1,7 @@ getSmallIntTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : int) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?int { return $value === null ? null : (int) $value; } - /** - * {@inheritDoc} - */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::INTEGER; } diff --git a/doctrine/dbal/src/Types/StringType.php b/doctrine/dbal/src/Types/StringType.php index 1992e8fa4..d3f92aaaa 100644 --- a/doctrine/dbal/src/Types/StringType.php +++ b/doctrine/dbal/src/Types/StringType.php @@ -1,5 +1,7 @@ getStringTypeDeclarationSQL($column); } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::STRING; - } } diff --git a/doctrine/dbal/src/Types/TextType.php b/doctrine/dbal/src/Types/TextType.php index d060bb2da..a682be5bc 100644 --- a/doctrine/dbal/src/Types/TextType.php +++ b/doctrine/dbal/src/Types/TextType.php @@ -1,5 +1,7 @@ getClobTypeDeclarationSQL($column); } - /** - * {@inheritDoc} - */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed { return is_resource($value) ? stream_get_contents($value) : $value; } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Types::TEXT; - } } diff --git a/doctrine/dbal/src/Types/TimeImmutableType.php b/doctrine/dbal/src/Types/TimeImmutableType.php index 9373f5913..c1c24d8b2 100644 --- a/doctrine/dbal/src/Types/TimeImmutableType.php +++ b/doctrine/dbal/src/Types/TimeImmutableType.php @@ -1,34 +1,35 @@ getTimeTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; @@ -38,23 +39,21 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) return $value->format($platform->getTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTimeImmutable::class], ); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable { if ($value === null || $value instanceof DateTimeImmutable) { return $value; @@ -66,27 +65,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $dateTime; } - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getTimeFormatString(), ); } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/TimeType.php b/doctrine/dbal/src/Types/TimeType.php index 7356fc206..0f96fd561 100644 --- a/doctrine/dbal/src/Types/TimeType.php +++ b/doctrine/dbal/src/Types/TimeType.php @@ -1,92 +1,57 @@ getTimeTypeDeclarationSQL($column); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : string) * * @template T */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string { if ($value === null) { return $value; } - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - TimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value instanceof DateTimeInterface) { + if ($value instanceof DateTime) { return $value->format($platform->getTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); + throw InvalidType::new($value, static::class, ['null', DateTime::class]); } /** - * {@inheritDoc} - * * @param T $value * - * @return (T is null ? null : DateTimeInterface) + * @return (T is null ? null : DateTime) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTime { - if ($value instanceof DateTimeImmutable) { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/6017', - 'Passing an instance of %s is deprecated, use %s::%s() instead.', - get_class($value), - TimeImmutableType::class, - __FUNCTION__, - ); - } - - if ($value === null || $value instanceof DateTimeInterface) { + if ($value === null || $value instanceof DateTime) { return $value; } @@ -95,9 +60,9 @@ public function convertToPHPValue($value, AbstractPlatform $platform) return $dateTime; } - throw ConversionException::conversionFailedFormat( + throw InvalidFormat::new( $value, - $this->getName(), + static::class, $platform->getTimeFormatString(), ); } diff --git a/doctrine/dbal/src/Types/Type.php b/doctrine/dbal/src/Types/Type.php index 7613811e9..9e058b13d 100644 --- a/doctrine/dbal/src/Types/Type.php +++ b/doctrine/dbal/src/Types/Type.php @@ -1,14 +1,14 @@ ArrayType::class, Types::ASCII_STRING => AsciiStringType::class, Types::BIGINT => BigIntType::class, Types::BINARY => BinaryType::class, @@ -39,7 +38,6 @@ abstract class Type Types::GUID => GuidType::class, Types::INTEGER => IntegerType::class, Types::JSON => JsonType::class, - Types::OBJECT => ObjectType::class, Types::SIMPLE_ARRAY => SimpleArrayType::class, Types::SMALLINT => SmallIntType::class, Types::STRING => StringType::class, @@ -66,7 +64,7 @@ final public function __construct() * * @throws ConversionException */ - public function convertToDatabaseValue($value, AbstractPlatform $platform) + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed { return $value; } @@ -82,7 +80,7 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform) * * @throws ConversionException */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed { return $value; } @@ -90,22 +88,10 @@ public function convertToPHPValue($value, AbstractPlatform $platform) /** * Gets the SQL declaration snippet for a column of this type. * - * @param mixed[] $column The column definition - * @param AbstractPlatform $platform The currently used database platform. - * - * @return string + * @param array $column The column definition + * @param AbstractPlatform $platform The currently used database platform. */ - abstract public function getSQLDeclaration(array $column, AbstractPlatform $platform); - - /** - * Gets the name of this type. - * - * @deprecated this method will be removed in Doctrine DBAL 4.0, - * use {@see TypeRegistry::lookupName()} instead. - * - * @return string - */ - abstract public function getName(); + abstract public function getSQLDeclaration(array $column, AbstractPlatform $platform): string; final public static function getTypeRegistry(): TypeRegistry { @@ -125,15 +111,12 @@ private static function createTypeRegistry(): TypeRegistry /** * Factory method to create type instances. - * Type instances are implemented as flyweights. - * - * @param string $name The name of the type (as returned by getName()). * - * @return Type + * @param string $name The name of the type. * * @throws Exception */ - public static function getType($name) + public static function getType(string $name): self { return self::getTypeRegistry()->get($name); } @@ -151,14 +134,12 @@ public static function lookupName(self $type): string /** * Adds a custom type to the type map. * - * @param string $name The name of the type. This should correspond to what getName() returns. + * @param string $name The name of the type. * @param class-string $className The class name of the custom type. * - * @return void - * * @throws Exception */ - public static function addType($name, $className) + public static function addType(string $name, string $className): void { self::getTypeRegistry()->register($name, new $className()); } @@ -170,7 +151,7 @@ public static function addType($name, $className) * * @return bool TRUE if type is supported; FALSE otherwise. */ - public static function hasType($name) + public static function hasType(string $name): bool { return self::getTypeRegistry()->has($name); } @@ -178,14 +159,11 @@ public static function hasType($name) /** * Overrides an already defined type to use a different implementation. * - * @param string $name * @param class-string $className * - * @return void - * * @throws Exception */ - public static function overrideType($name, $className) + public static function overrideType(string $name, string $className): void { self::getTypeRegistry()->override($name, new $className()); } @@ -193,12 +171,8 @@ public static function overrideType($name, $className) /** * Gets the (preferred) binding type for values of this type that * can be used when binding parameters to prepared statements. - * - * This method should return one of the {@see ParameterType} constants. - * - * @return int */ - public function getBindingType() + public function getBindingType(): ParameterType { return ParameterType::STRING; } @@ -209,55 +183,28 @@ public function getBindingType() * * @return array */ - public static function getTypesMap() + public static function getTypesMap(): array { return array_map( static function (Type $type): string { - return get_class($type); + return $type::class; }, self::getTypeRegistry()->getMap(), ); } - /** - * Does working with this column require SQL conversion functions? - * - * This is a metadata function that is required for example in the ORM. - * Usage of {@see convertToDatabaseValueSQL} and - * {@see convertToPHPValueSQL} works for any type and mostly - * does nothing. This method can additionally be used for optimization purposes. - * - * @deprecated Consumers should call {@see convertToDatabaseValueSQL} and {@see convertToPHPValueSQL} - * regardless of the type. - * - * @return bool - */ - public function canRequireSQLConversion() - { - return false; - } - /** * Modifies the SQL expression (identifier, parameter) to convert to a database value. - * - * @param string $sqlExpr - * - * @return string */ - public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) + public function convertToDatabaseValueSQL(string $sqlExpr, AbstractPlatform $platform): string { return $sqlExpr; } /** * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. - * - * @param string $sqlExpr - * @param AbstractPlatform $platform - * - * @return string */ - public function convertToPHPValueSQL($sqlExpr, $platform) + public function convertToPHPValueSQL(string $sqlExpr, AbstractPlatform $platform): string { return $sqlExpr; } @@ -265,32 +212,10 @@ public function convertToPHPValueSQL($sqlExpr, $platform) /** * Gets an array of database types that map to this Doctrine type. * - * @return string[] + * @return array */ - public function getMappedDatabaseTypes(AbstractPlatform $platform) + public function getMappedDatabaseTypes(AbstractPlatform $platform): array { return []; } - - /** - * If this Doctrine Type maps to an already mapped database type, - * reverse schema engineering can't tell them apart. You need to mark - * one of those types as commented, which will have Doctrine use an SQL - * comment to typehint the actual Doctrine Type. - * - * @deprecated - * - * @return bool - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return false; - } } diff --git a/doctrine/dbal/src/Types/TypeRegistry.php b/doctrine/dbal/src/Types/TypeRegistry.php index 9b64c6faf..5ef36c393 100644 --- a/doctrine/dbal/src/Types/TypeRegistry.php +++ b/doctrine/dbal/src/Types/TypeRegistry.php @@ -5,12 +5,16 @@ namespace Doctrine\DBAL\Types; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Types\Exception\TypeAlreadyRegistered; +use Doctrine\DBAL\Types\Exception\TypeNotFound; +use Doctrine\DBAL\Types\Exception\TypeNotRegistered; +use Doctrine\DBAL\Types\Exception\TypesAlreadyExists; +use Doctrine\DBAL\Types\Exception\UnknownColumnType; use function spl_object_id; /** * The type registry is responsible for holding a map of all known DBAL types. - * The types are stored using the flyweight pattern so that one type only exists as exactly one instance. */ final class TypeRegistry { @@ -38,7 +42,7 @@ public function get(string $name): Type { $type = $this->instances[$name] ?? null; if ($type === null) { - throw Exception::unknownColumnType($name); + throw UnknownColumnType::new($name); } return $type; @@ -54,7 +58,7 @@ public function lookupName(Type $type): string $name = $this->findTypeName($type); if ($name === null) { - throw Exception::typeNotRegistered($type); + throw TypeNotRegistered::new($type); } return $name; @@ -76,11 +80,11 @@ public function has(string $name): bool public function register(string $name, Type $type): void { if (isset($this->instances[$name])) { - throw Exception::typeExists($name); + throw TypesAlreadyExists::new($name); } if ($this->findTypeName($type) !== null) { - throw Exception::typeAlreadyRegistered($type); + throw TypeAlreadyRegistered::new($type); } $this->instances[$name] = $type; @@ -96,11 +100,11 @@ public function override(string $name, Type $type): void { $origType = $this->instances[$name] ?? null; if ($origType === null) { - throw Exception::typeNotFound($name); + throw TypeNotFound::new($name); } if (($this->findTypeName($type) ?? $name) !== $name) { - throw Exception::typeAlreadyRegistered($type); + throw TypeAlreadyRegistered::new($type); } unset($this->instancesReverseIndex[spl_object_id($origType)]); diff --git a/doctrine/dbal/src/Types/Types.php b/doctrine/dbal/src/Types/Types.php index 54b0dfecc..07cf1ffef 100644 --- a/doctrine/dbal/src/Types/Types.php +++ b/doctrine/dbal/src/Types/Types.php @@ -9,9 +9,6 @@ */ final class Types { - /** @deprecated Use {@link Types::JSON} instead. */ - public const ARRAY = 'array'; - public const ASCII_STRING = 'ascii_string'; public const BIGINT = 'bigint'; public const BINARY = 'binary'; @@ -29,16 +26,12 @@ final class Types public const GUID = 'guid'; public const INTEGER = 'integer'; public const JSON = 'json'; - - /** @deprecated Use {@link Types::JSON} instead. */ - public const OBJECT = 'object'; - - public const SIMPLE_ARRAY = 'simple_array'; - public const SMALLINT = 'smallint'; - public const STRING = 'string'; - public const TEXT = 'text'; - public const TIME_MUTABLE = 'time'; - public const TIME_IMMUTABLE = 'time_immutable'; + public const SIMPLE_ARRAY = 'simple_array'; + public const SMALLINT = 'smallint'; + public const STRING = 'string'; + public const TEXT = 'text'; + public const TIME_MUTABLE = 'time'; + public const TIME_IMMUTABLE = 'time_immutable'; /** @codeCoverageIgnore */ private function __construct() diff --git a/doctrine/dbal/src/Types/VarDateTimeImmutableType.php b/doctrine/dbal/src/Types/VarDateTimeImmutableType.php index cd6fa890b..67c2cd8b8 100644 --- a/doctrine/dbal/src/Types/VarDateTimeImmutableType.php +++ b/doctrine/dbal/src/Types/VarDateTimeImmutableType.php @@ -1,35 +1,28 @@ format($platform->getDateTimeFormatString()); } - throw ConversionException::conversionFailedInvalidType( + throw InvalidType::new( $value, - $this->getName(), + static::class, ['null', DateTimeImmutable::class], ); } /** - * {@inheritDoc} - * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ - public function convertToPHPValue($value, AbstractPlatform $platform) + public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?DateTimeImmutable { if ($value === null || $value instanceof DateTimeImmutable) { return $value; @@ -64,26 +55,9 @@ public function convertToPHPValue($value, AbstractPlatform $platform) try { $dateTime = new DateTimeImmutable($value); } catch (Exception $e) { - throw ConversionException::conversionFailed($value, $this->getName(), $e); + throw ValueNotConvertible::new($value, DateTimeImmutable::class, $e->getMessage(), $e); } return $dateTime; } - - /** - * {@inheritDoc} - * - * @deprecated - */ - public function requiresSQLCommentHint(AbstractPlatform $platform) - { - Deprecation::triggerIfCalledFromOutside( - 'doctrine/dbal', - 'https://github.com/doctrine/dbal/pull/5509', - '%s is deprecated.', - __METHOD__, - ); - - return true; - } } diff --git a/doctrine/dbal/src/Types/VarDateTimeType.php b/doctrine/dbal/src/Types/VarDateTimeType.php index 35ad40328..55dec4930 100644 --- a/doctrine/dbal/src/Types/VarDateTimeType.php +++ b/doctrine/dbal/src/Types/VarDateTimeType.php @@ -1,10 +1,12 @@ getName(), $e); + throw ValueNotConvertible::new($value, DateTime::class, $e->getMessage(), $e); } return $dateTime; diff --git a/doctrine/dbal/src/VersionAwarePlatformDriver.php b/doctrine/dbal/src/VersionAwarePlatformDriver.php deleted file mode 100644 index ffcfcd636..000000000 --- a/doctrine/dbal/src/VersionAwarePlatformDriver.php +++ /dev/null @@ -1,30 +0,0 @@ - => - * - * @var array - */ - private $listeners = []; - - /** - * Dispatches an event to all registered listeners. - * - * @param string $eventName The name of the event to dispatch. The name of the event is - * the name of the method that is invoked on listeners. - * @param EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners. - * If not supplied, the single empty EventArgs instance is used. - * - * @return void - */ - public function dispatchEvent($eventName, ?EventArgs $eventArgs = null) - { - if (! isset($this->listeners[$eventName])) { - return; - } - - $eventArgs = $eventArgs ?? EventArgs::getEmptyInstance(); - - foreach ($this->listeners[$eventName] as $listener) { - $listener->$eventName($eventArgs); - } - } - - /** - * Gets the listeners of a specific event. - * - * @param string|null $event The name of the event. - * - * @return object[]|array The event listeners for the specified event, or all event listeners. - * @psalm-return ($event is null ? array : object[]) - */ - public function getListeners($event = null) - { - if ($event === null) { - Deprecation::trigger( - 'doctrine/event-manager', - 'https://github.com/doctrine/event-manager/pull/50', - 'Calling %s without an event name is deprecated. Call getAllListeners() instead.', - __METHOD__ - ); - - return $this->getAllListeners(); - } - - return $this->listeners[$event] ?? []; - } - - /** - * Gets all listeners keyed by event name. - * - * @return array The event listeners for the specified event, or all event listeners. - */ - public function getAllListeners(): array - { - return $this->listeners; - } - - /** - * Checks whether an event has any registered listeners. - * - * @param string $event - * - * @return bool TRUE if the specified event has any listeners, FALSE otherwise. - */ - public function hasListeners($event) - { - return ! empty($this->listeners[$event]); - } - - /** - * Adds an event listener that listens on the specified events. - * - * @param string|string[] $events The event(s) to listen on. - * @param object $listener The listener object. - * - * @return void - */ - public function addEventListener($events, $listener) - { - // Picks the hash code related to that listener - $hash = spl_object_hash($listener); - - foreach ((array) $events as $event) { - // Overrides listener if a previous one was associated already - // Prevents duplicate listeners on same event (same instance only) - $this->listeners[$event][$hash] = $listener; - } - } - - /** - * Removes an event listener from the specified events. - * - * @param string|string[] $events - * @param object $listener - * - * @return void - */ - public function removeEventListener($events, $listener) - { - // Picks the hash code related to that listener - $hash = spl_object_hash($listener); - - foreach ((array) $events as $event) { - unset($this->listeners[$event][$hash]); - } - } - - /** - * Adds an EventSubscriber. The subscriber is asked for all the events it is - * interested in and added as a listener for these events. - * - * @param EventSubscriber $subscriber The subscriber. - * - * @return void - */ - public function addEventSubscriber(EventSubscriber $subscriber) - { - $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); - } - - /** - * Removes an EventSubscriber. The subscriber is asked for all the events it is - * interested in and removed as a listener for these events. - * - * @param EventSubscriber $subscriber The subscriber. - * - * @return void - */ - public function removeEventSubscriber(EventSubscriber $subscriber) - { - $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); - } -} diff --git a/doctrine/event-manager/src/EventSubscriber.php b/doctrine/event-manager/src/EventSubscriber.php deleted file mode 100644 index 89cef558a..000000000 --- a/doctrine/event-manager/src/EventSubscriber.php +++ /dev/null @@ -1,21 +0,0 @@ -