Unverified Commit 430dce61 authored by Grégoire Paris's avatar Grégoire Paris

Merge branch '2.11.x' into 3.0.x

parents 655b6bfd 0a2e2f1e
......@@ -23,11 +23,11 @@ jobs:
uses: "actions/cache@v1.0.3"
with:
path: "~/.composer/cache"
key: "composer-${{ hashFiles('composer.json') }}"
key: "composer-${{ hashFiles('composer.lock') }}"
restore-keys: "composer-"
- name: "Install dependencies with composer"
run: "composer update --no-interaction --no-progress --no-suggest"
run: "composer install --no-interaction --no-progress --no-suggest"
- name: Psalm
run: "vendor/bin/psalm"
......@@ -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": "b1aaf618f2a43579f70e9429ed20a75c",
"content-hash": "893d32dea2abc8c66017ee58e435fac8",
"packages": [
{
"name": "doctrine/cache",
......@@ -528,28 +528,28 @@
},
{
"name": "doctrine/coding-standard",
"version": "6.0.0",
"version": "7.0.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/coding-standard.git",
"reference": "d33f69eb98b25aa51ffe3a909f0ec77000af4701"
"reference": "d8a60ec4da68025c42795b714f66e277dd3e11de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/coding-standard/zipball/d33f69eb98b25aa51ffe3a909f0ec77000af4701",
"reference": "d33f69eb98b25aa51ffe3a909f0ec77000af4701",
"url": "https://api.github.com/repos/doctrine/coding-standard/zipball/d8a60ec4da68025c42795b714f66e277dd3e11de",
"reference": "d8a60ec4da68025c42795b714f66e277dd3e11de",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
"php": "^7.1",
"slevomat/coding-standard": "^5.0",
"squizlabs/php_codesniffer": "^3.4.0"
"php": "^7.2",
"slevomat/coding-standard": "^6.0",
"squizlabs/php_codesniffer": "^3.5.3"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "6.0.x-dev"
"dev-master": "7.0.x-dev"
}
},
"autoload": {
......@@ -585,7 +585,7 @@
"standard",
"style"
],
"time": "2019-03-15T12:45:47+00:00"
"time": "2019-12-11T07:59:21+00:00"
},
{
"name": "doctrine/instantiator",
......@@ -1283,16 +1283,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "0.3.5",
"version": "0.4.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "8c4ef2aefd9788238897b678a985e1d5c8df6db4"
"reference": "d8d9d4645379e677466d407034436bb155b11c65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/8c4ef2aefd9788238897b678a985e1d5c8df6db4",
"reference": "8c4ef2aefd9788238897b678a985e1d5c8df6db4",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/d8d9d4645379e677466d407034436bb155b11c65",
"reference": "d8d9d4645379e677466d407034436bb155b11c65",
"shasum": ""
},
"require": {
......@@ -1300,18 +1300,20 @@
},
"require-dev": {
"consistence/coding-standard": "^3.5",
"ergebnis/composer-normalize": "^2.0.2",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phing/phing": "^2.16.0",
"phpstan/phpstan": "^0.10",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12.19",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/phpunit": "^6.3",
"slevomat/coding-standard": "^4.7.2",
"squizlabs/php_codesniffer": "^3.3.2",
"symfony/process": "^3.4 || ^4.0"
"symfony/process": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.3-dev"
"dev-master": "0.4-dev"
}
},
"autoload": {
......@@ -1326,7 +1328,7 @@
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"time": "2019-06-07T19:13:52+00:00"
"time": "2020-04-13T16:28:46+00:00"
},
{
"name": "phpstan/phpstan",
......@@ -2529,32 +2531,39 @@
},
{
"name": "slevomat/coding-standard",
"version": "5.0.2",
"version": "6.3.3",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "223f02b6193fe47b7b483bfa5bf75693535482dd"
"reference": "b905a82255749de847fd4de607c7a4c8163f058d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/223f02b6193fe47b7b483bfa5bf75693535482dd",
"reference": "223f02b6193fe47b7b483bfa5bf75693535482dd",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b905a82255749de847fd4de607c7a4c8163f058d",
"reference": "b905a82255749de847fd4de607c7a4c8163f058d",
"shasum": ""
},
"require": {
"php": "^7.1",
"phpstan/phpdoc-parser": "^0.3.1",
"squizlabs/php_codesniffer": "^3.4.0"
"phpstan/phpdoc-parser": "0.4.0 - 0.4.4",
"squizlabs/php_codesniffer": "^3.5.5"
},
"require-dev": {
"jakub-onderka/php-parallel-lint": "1.0.0",
"phing/phing": "2.16.1",
"phpstan/phpstan": "0.11.1",
"phpstan/phpstan-phpunit": "0.11",
"phpstan/phpstan-strict-rules": "0.11",
"phpunit/phpunit": "8.0.0"
"dealerdirect/phpcodesniffer-composer-installer": "0.6.2",
"phing/phing": "2.16.3",
"php-parallel-lint/php-parallel-lint": "1.2.0",
"phpstan/phpstan": "0.12.19",
"phpstan/phpstan-deprecation-rules": "0.12.2",
"phpstan/phpstan-phpunit": "0.12.8",
"phpstan/phpstan-strict-rules": "0.12.2",
"phpunit/phpunit": "7.5.20|8.5.2|9.1.2"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"autoload": {
"psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard"
......@@ -2565,20 +2574,30 @@
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"time": "2019-03-12T20:26:36+00:00"
"funding": [
{
"url": "https://github.com/kukulich",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
"type": "tidelift"
}
],
"time": "2020-04-28T07:15:08+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.3",
"version": "3.5.5",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb"
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"reference": "73e2e7f57d958e7228fce50dc0c61f58f017f9f6",
"shasum": ""
},
"require": {
......@@ -2616,7 +2635,7 @@
"phpcs",
"standards"
],
"time": "2019-12-04T04:46:47+00:00"
"time": "2020-04-17T01:09:41+00:00"
},
{
"name": "symfony/console",
......
<?xml version="1.0"?>
<ruleset>
<ruleset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd"
>
<arg name="basepath" value="."/>
<arg name="extensions" value="php"/>
<arg name="parallel" value="80"/>
......@@ -17,15 +20,32 @@
<exclude name="SlevomatCodingStandard.TypeHints.DeclareStrictTypes"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming"/>
<exclude name="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming"/>
<exclude name="SlevomatCodingStandard.ControlStructures.ControlStructureSpacing.IncorrectLinesCountAfterLastControlStructure"/>
<exclude name="SlevomatCodingStandard.Classes.DisallowLateStaticBindingForConstants.DisallowedLateStaticBindingForConstant"/>
<exclude name="SlevomatCodingStandard.ControlStructures.ControlStructureSpacing.IncorrectLinesCountAfterLastControlStructure"/>
<!-- https://github.com/slevomat/coding-standard/issues/867 -->
<exclude name="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing.IncorrectLinesCountAfterLastControlStructure"/>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2937 -->
<exclude name="Squiz.Arrays.ArrayDeclaration.ValueNoNewline"/>
<exclude name="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore"/>
</rule>
<!-- Disable the rules that will require PHP 7.4 -->
<rule ref="SlevomatCodingStandard.TypeHints.PropertyTypeHint">
<properties>
<property name="enableNativeTypeHint" value="false"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint">
<rule ref="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint">
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
......@@ -45,6 +65,11 @@
<exclude-pattern>tests/Tools/TestAsset/*</exclude-pattern>
</rule>
<!-- https://github.com/slevomat/coding-standard/issues/868 -->
<rule ref="SlevomatCodingStandard.PHP.UselessParentheses.UselessParentheses">
<exclude-pattern>ci/continuousphp/bootstrap.php</exclude-pattern>
</rule>
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/2099 -->
<rule ref="Squiz.Commenting.FunctionComment.InvalidNoReturn">
<exclude-pattern>src/Platforms/AbstractPlatform.php</exclude-pattern>
......@@ -52,6 +77,31 @@
<exclude-pattern>tests/Platforms/AbstractPlatformTestCase.php</exclude-pattern>
</rule>
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/2838 -->
<rule ref="Squiz.Commenting.FunctionComment.SpacingAfter">
<exclude-pattern>src/Driver/AbstractMySQLDriver.php</exclude-pattern>
</rule>
<rule ref="Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps">
<!-- This test contains a fixture class that should use snake case -->
<exclude-pattern>tests/Functional/DataAccessTest.php</exclude-pattern>
</rule>
<!-- https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<!--
This file uses the return value db2_server_info(), which does not follow conventions
phpcs wrongly complains about it, and that has been reported here:
https://github.com/squizlabs/PHP_CodeSniffer/issues/2950
-->
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/issues/2837 -->
<exclude-pattern>src/SQLParserUtils.php</exclude-pattern>
<exclude-pattern>src/Tools/Dumper.php</exclude-pattern>
<exclude-pattern>tests/Driver/StatementIteratorTest.php</exclude-pattern>
<exclude-pattern>tests/Tools/DumperTest.php</exclude-pattern>
</rule>
<!-- some statement classes close cursor using an empty while-loop -->
<rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedWhile">
<exclude-pattern>src/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern>
......@@ -62,22 +112,15 @@
<exclude-pattern>src/Driver/SQLSrv/SQLSrvStatement.php</exclude-pattern>
</rule>
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/2165 -->
<rule ref="Squiz.Arrays.ArrayDeclaration.SpaceBeforeComma">
<exclude-pattern>tests/Platforms/OraclePlatformTest.php</exclude-pattern>
<exclude-pattern>tests/SQLParserUtilsTest.php</exclude-pattern>
</rule>
<!-- see https://github.com/doctrine/dbal/issues/3377 -->
<rule ref="SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator">
<exclude-pattern>src/Schema/Comparator.php</exclude-pattern>
</rule>
<!-- see https://github.com/slevomat/coding-standard/issues/737 -->
<rule ref="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.IncorrectReturnTypeHint">
<exclude-pattern>src/Types/ArrayType.php</exclude-pattern>
<exclude-pattern>src/Types/ObjectType.php</exclude-pattern>
<exclude-pattern>tests/Driver/Mysqli/MysqliConnectionTest.php</exclude-pattern>
<!-- DB2_AUTOCOMMIT_ON/DB2_AUTOCOMMIT_OFF are of int type but db2_autocommit() incorrectly expects bool,
see https://bugs.php.net/bug.php?id=77591 -->
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing">
<exclude-pattern>src/Driver/IBMDB2/DB2Connection.php</exclude-pattern>
</rule>
<!-- The SQLSRV_* functions are defined in the upper case by the sqlsrv extension and violate the standard
......
......@@ -74,6 +74,17 @@ parameters:
message: '~^Cannot cast array<string>\|bool\|string\|null to int\.$~'
path: %currentWorkingDirectory%/src/Tools/Console/Command/RunSqlCommand.php
# Requires a release of https://github.com/JetBrains/phpstorm-stubs/pull/732
-
message: '~^Access to undefined constant PDO::PGSQL_ATTR_DISABLE_PREPARES\.$~'
path: %currentWorkingDirectory%/src/Driver/PDOPgSql/Driver.php
# False Positive
- '~Strict comparison using === between 1 and 2 will always evaluate to false~'
# Needs Generics
- '~Method Doctrine\\DBAL\\Schema\\SchemaDiff::getNewTablesSortedByDependencies\(\) should return array<Doctrine\\DBAL\\Schema\\Table> but returns array<object>.~'
# https://github.com/phpstan/phpstan/issues/3134
-
message: '~^Call to static method PHPUnit\\Framework\\Assert::assertSame\(\) with Doctrine\\DBAL\\Types\\Type and Doctrine\\DBAL\\Types\\Type will always evaluate to true\.$~'
......
......@@ -10,7 +10,7 @@ use Doctrine\DBAL\DBALException;
class CacheException extends DBALException
{
/**
* @return \Doctrine\DBAL\Cache\CacheException
* @return CacheException
*/
public static function noCacheKey()
{
......@@ -18,7 +18,7 @@ class CacheException extends DBALException
}
/**
* @return \Doctrine\DBAL\Cache\CacheException
* @return CacheException
*/
public static function noResultDriverConfigured()
{
......
......@@ -92,7 +92,7 @@ class QueryCacheProfile
}
/**
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
* @return QueryCacheProfile
*/
public function setResultCacheDriver(Cache $cache)
{
......@@ -102,7 +102,7 @@ class QueryCacheProfile
/**
* @param string|null $cacheKey
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
* @return QueryCacheProfile
*/
public function setCacheKey($cacheKey)
{
......@@ -112,7 +112,7 @@ class QueryCacheProfile
/**
* @param int $lifetime
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
* @return QueryCacheProfile
*/
public function setLifetime($lifetime)
{
......
......@@ -85,6 +85,7 @@ class ResultCacheStatement implements IteratorAggregate, ResultStatement
if ($data === false) {
$data = [];
}
$data[$this->realKey] = $this->data;
$this->resultCache->save($this->cacheKey, $data, $this->lifetime);
......
......@@ -984,9 +984,6 @@ class Connection implements DriverConnection
return $result;
}
/**
* {@inheritDoc}
*/
public function query(string $sql) : ResultStatement
{
$connection = $this->getWrappedConnection();
......@@ -1044,6 +1041,7 @@ class Connection implements DriverConnection
} else {
$stmt->execute($params);
}
$result = $stmt->rowCount();
} else {
$result = $connection->exec($query);
......@@ -1059,9 +1057,6 @@ class Connection implements DriverConnection
return $result;
}
/**
* {@inheritDoc}
*/
public function exec(string $statement) : int
{
$connection = $this->getWrappedConnection();
......@@ -1154,9 +1149,11 @@ class Connection implements DriverConnection
return $res;
} catch (Exception $e) {
$this->rollBack();
throw $e;
} catch (Throwable $e) {
$this->rollBack();
throw $e;
}
}
......@@ -1229,6 +1226,7 @@ class Connection implements DriverConnection
if ($logger !== null) {
$logger->startQuery('"SAVEPOINT"');
}
$this->createSavepoint($this->_getNestedTransactionSavePointName());
if ($logger !== null) {
$logger->stopQuery();
......@@ -1249,6 +1247,7 @@ class Connection implements DriverConnection
if ($this->transactionNestingLevel === 0) {
throw ConnectionException::noActiveTransaction();
}
if ($this->isRollbackOnly) {
throw ConnectionException::commitFailedRollbackOnly();
}
......@@ -1273,6 +1272,7 @@ class Connection implements DriverConnection
if ($logger !== null) {
$logger->startQuery('"RELEASE SAVEPOINT"');
}
$this->releaseSavepoint($this->_getNestedTransactionSavePointName());
if ($logger !== null) {
$logger->stopQuery();
......@@ -1329,6 +1329,7 @@ class Connection implements DriverConnection
if ($logger !== null) {
$logger->startQuery('"ROLLBACK"');
}
$this->transactionNestingLevel = 0;
$connection->rollBack();
$this->isRollbackOnly = false;
......@@ -1343,6 +1344,7 @@ class Connection implements DriverConnection
if ($logger !== null) {
$logger->startQuery('"ROLLBACK TO SAVEPOINT"');
}
$this->rollbackSavepoint($this->_getNestedTransactionSavePointName());
--$this->transactionNestingLevel;
if ($logger !== null) {
......@@ -1454,6 +1456,7 @@ class Connection implements DriverConnection
if ($this->transactionNestingLevel === 0) {
throw ConnectionException::noActiveTransaction();
}
$this->isRollbackOnly = true;
}
......@@ -1528,6 +1531,7 @@ class Connection implements DriverConnection
} else {
$stmt->bindValue($bindIndex, $value);
}
++$bindIndex;
}
} else {
......@@ -1557,6 +1561,7 @@ class Connection implements DriverConnection
if (is_string($type)) {
$type = Type::getType($type);
}
if ($type instanceof Type) {
$value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform());
$bindingType = $type->getBindingType();
......@@ -1596,6 +1601,7 @@ class Connection implements DriverConnection
} else {
$resolvedParams[$bindIndex] = $value;
}
++$bindIndex;
}
} else {
......
......@@ -8,7 +8,7 @@ namespace Doctrine\DBAL;
class ConnectionException extends DBALException
{
/**
* @return \Doctrine\DBAL\ConnectionException
* @return ConnectionException
*/
public static function commitFailedRollbackOnly()
{
......@@ -16,7 +16,7 @@ class ConnectionException extends DBALException
}
/**
* @return \Doctrine\DBAL\ConnectionException
* @return ConnectionException
*/
public static function noActiveTransaction()
{
......@@ -24,7 +24,7 @@ class ConnectionException extends DBALException
}
/**
* @return \Doctrine\DBAL\ConnectionException
* @return ConnectionException
*/
public static function savepointsNotSupported()
{
......@@ -32,7 +32,7 @@ class ConnectionException extends DBALException
}
/**
* @return \Doctrine\DBAL\ConnectionException
* @return ConnectionException
*/
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
{
......
......@@ -96,6 +96,7 @@ class MasterSlaveConnection extends Connection
if (! isset($params['slaves'], $params['master'])) {
throw new InvalidArgumentException('master or slaves configuration missing');
}
if (count($params['slaves']) === 0) {
throw new InvalidArgumentException('You have to configure at least one slaves.');
}
......@@ -302,9 +303,6 @@ class MasterSlaveConnection extends Connection
return parent::insert($tableName, $data, $types);
}
/**
* {@inheritDoc}
*/
public function exec(string $statement) : int
{
$this->connect('master');
......@@ -342,9 +340,6 @@ class MasterSlaveConnection extends Connection
parent::rollbackSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function query(string $sql) : ResultStatement
{
$this->connect('master');
......@@ -366,9 +361,6 @@ class MasterSlaveConnection extends Connection
return $statement;
}
/**
* {@inheritDoc}
*/
public function prepare(string $sql) : Statement
{
$this->connect('master');
......
......@@ -31,7 +31,7 @@ class DBALException extends Exception
/**
* @param string $method
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function notSupported($method)
{
......@@ -90,7 +90,7 @@ class DBALException extends Exception
}
/**
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function invalidPdoInstance()
{
......@@ -103,7 +103,7 @@ class DBALException extends Exception
/**
* @param string|null $url The URL that was provided in the connection parameters (if any).
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function driverRequired($url = null)
{
......@@ -125,7 +125,7 @@ class DBALException extends Exception
* @param string $unknownDriverName
* @param string[] $knownDrivers
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function unknownDriver($unknownDriverName, array $knownDrivers)
{
......@@ -145,6 +145,7 @@ class DBALException extends Exception
if (count($params) > 0) {
$msg .= ' with params ' . self::formatParameters($params);
}
$msg .= ":\n\n" . $driverEx->getMessage();
return static::wrapException($driver, $driverEx, $msg);
......@@ -166,6 +167,7 @@ class DBALException extends Exception
if ($driverEx instanceof DriverException) {
return $driverEx;
}
if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverExceptionInterface) {
return $driver->convertException($msg, $driverEx);
}
......@@ -202,7 +204,7 @@ class DBALException extends Exception
/**
* @param string $wrapperClass
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function invalidWrapperClass($wrapperClass)
{
......@@ -213,7 +215,7 @@ class DBALException extends Exception
/**
* @param string $driverClass
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function invalidDriverClass($driverClass)
{
......@@ -223,7 +225,7 @@ class DBALException extends Exception
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function invalidTableName($tableName)
{
......@@ -233,7 +235,7 @@ class DBALException extends Exception
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function noColumnsSpecifiedForTable($tableName)
{
......@@ -241,7 +243,7 @@ class DBALException extends Exception
}
/**
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function limitOffsetInvalid()
{
......@@ -251,7 +253,7 @@ class DBALException extends Exception
/**
* @param string $name
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function typeExists($name)
{
......@@ -261,7 +263,7 @@ class DBALException extends Exception
/**
* @param string $name
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function unknownColumnType($name)
{
......@@ -277,7 +279,7 @@ class DBALException extends Exception
/**
* @param string $name
*
* @return \Doctrine\DBAL\DBALException
* @return DBALException
*/
public static function typeNotFound($name)
{
......
......@@ -31,8 +31,10 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
switch ($exception->getErrorCode()) {
case '1213':
return new Exception\DeadlockException($message, $exception);
case '1205':
return new Exception\LockWaitTimeoutException($message, $exception);
case '1050':
return new Exception\TableExistsException($message, $exception);
......@@ -122,6 +124,7 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
if (version_compare($oracleMysqlVersion, '8', '>=')) {
return new MySQL80Platform();
}
if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) {
return new MySQL57Platform();
}
......@@ -150,6 +153,7 @@ abstract class AbstractMySQLDriver implements ExceptionConverterDriver, VersionA
'<major_version>.<minor_version>.<patch_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? null;
......
......@@ -29,6 +29,7 @@ abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, Ver
case '40001':
case '40P01':
return new Exception\DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
......@@ -37,6 +38,7 @@ abstract class AbstractPostgreSQLDriver implements ExceptionConverterDriver, Ver
}
break;
case '23502':
return new Exception\NotNullConstraintViolationException($message, $exception);
......
......@@ -27,31 +27,41 @@ abstract class AbstractSQLAnywhereDriver implements ExceptionConverterDriver, Ve
case '-307':
case '-684':
return new Exception\DeadlockException($message, $exception);
case '-210':
case '-1175':
case '-1281':
return new Exception\LockWaitTimeoutException($message, $exception);
case '-100':
case '-103':
case '-832':
return new Exception\ConnectionException($message, $exception);
case '-143':
return new Exception\InvalidFieldNameException($message, $exception);
case '-193':
case '-196':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '-194':
case '-198':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case '-144':
return new Exception\NonUniqueFieldNameException($message, $exception);
case '-184':
case '-195':
return new Exception\NotNullConstraintViolationException($message, $exception);
case '-131':
return new Exception\SyntaxErrorException($message, $exception);
case '-110':
return new Exception\TableExistsException($message, $exception);
case '-141':
case '-1041':
return new Exception\TableNotFoundException($message, $exception);
......
......@@ -67,6 +67,10 @@ abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver
return new Exception\ConnectionException($message, $exception);
}
if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) {
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
}
return new Exception\DriverException($message, $exception);
}
......
......@@ -7,8 +7,6 @@ use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use stdClass;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
use function assert;
use function db2_autocommit;
use function db2_commit;
......@@ -25,6 +23,8 @@ use function db2_rollback;
use function db2_server_info;
use function db2_stmt_errormsg;
use function is_bool;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
class DB2Connection implements ServerInfoAwareConnection
{
......@@ -61,8 +61,8 @@ class DB2Connection implements ServerInfoAwareConnection
*/
public function getServerVersion()
{
/** @var stdClass $serverInfo */
$serverInfo = db2_server_info($this->conn);
assert($serverInfo instanceof stdClass);
return $serverInfo->DBMS_VER;
}
......@@ -75,9 +75,6 @@ class DB2Connection implements ServerInfoAwareConnection
return false;
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : DriverStatement
{
$stmt = @db2_prepare($this->conn, $sql);
......@@ -88,9 +85,6 @@ class DB2Connection implements ServerInfoAwareConnection
return new DB2Statement($stmt);
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
$stmt = $this->prepare($sql);
......@@ -113,9 +107,6 @@ class DB2Connection implements ServerInfoAwareConnection
return "'" . $input . "'";
}
/**
* {@inheritdoc}
*/
public function exec(string $statement) : int
{
$stmt = @db2_exec($this->conn, $statement);
......
......@@ -7,11 +7,6 @@ use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
use function assert;
use function db2_bind_param;
use function db2_execute;
......@@ -32,6 +27,11 @@ use function ksort;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function tmpfile;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
class DB2Statement implements IteratorAggregate, Statement
{
......@@ -275,7 +275,9 @@ class DB2Statement implements IteratorAggregate, Statement
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
}
break;
default:
while (($row = $this->fetch($fetchMode)) !== false) {
$rows[] = $row;
......@@ -299,9 +301,6 @@ class DB2Statement implements IteratorAggregate, Statement
return $row[0] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
return @db2_num_rows($this->stmt);
......
......@@ -8,12 +8,6 @@ use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use mysqli;
use const MYSQLI_INIT_COMMAND;
use const MYSQLI_OPT_CONNECT_TIMEOUT;
use const MYSQLI_OPT_LOCAL_INFILE;
use const MYSQLI_READ_DEFAULT_FILE;
use const MYSQLI_READ_DEFAULT_GROUP;
use const MYSQLI_SERVER_PUBLIC_KEY;
use function defined;
use function floor;
use function in_array;
......@@ -26,6 +20,12 @@ use function restore_error_handler;
use function set_error_handler;
use function sprintf;
use function stripos;
use const MYSQLI_INIT_COMMAND;
use const MYSQLI_OPT_CONNECT_TIMEOUT;
use const MYSQLI_OPT_LOCAL_INFILE;
use const MYSQLI_READ_DEFAULT_FILE;
use const MYSQLI_READ_DEFAULT_GROUP;
use const MYSQLI_SERVER_PUBLIC_KEY;
class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
{
......@@ -129,17 +129,11 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
return false;
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : DriverStatement
{
return new MysqliStatement($this->conn, $sql);
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
$stmt = $this->prepare($sql);
......@@ -156,9 +150,6 @@ class MysqliConnection implements PingableConnection, ServerInfoAwareConnection
return "'" . $this->conn->escape_string($input) . "'";
}
/**
* {@inheritdoc}
*/
public function exec(string $statement) : int
{
if ($this->conn->query($statement) === false) {
......
......@@ -223,6 +223,7 @@ class MysqliStatement implements IteratorAggregate, Statement
if (get_resource_type($value) !== 'stream') {
throw new InvalidArgumentException('Resources passed with the LARGE_OBJECT parameter type must be stream resources.');
}
$streams[$parameter] = $value;
$values[$parameter] = null;
continue;
......@@ -413,9 +414,6 @@ class MysqliStatement implements IteratorAggregate, Statement
return true;
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
if ($this->_columnNames === false) {
......
......@@ -4,7 +4,7 @@ namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use const OCI_DEFAULT;
use const OCI_NO_AUTO_COMMIT;
/**
* A Doctrine DBAL driver for the Oracle OCI8 PHP extensions.
......@@ -22,7 +22,7 @@ class Driver extends AbstractOracleDriver
(string) $password,
$this->_constructDsn($params),
$params['charset'] ?? '',
$params['sessionMode'] ?? OCI_DEFAULT,
$params['sessionMode'] ?? OCI_NO_AUTO_COMMIT,
$params['persistent'] ?? false
);
} catch (OCI8Exception $e) {
......
......@@ -8,9 +8,6 @@ use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use UnexpectedValueException;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_DEFAULT;
use const OCI_NO_AUTO_COMMIT;
use function addcslashes;
use function is_float;
use function is_int;
......@@ -23,6 +20,8 @@ use function oci_server_version;
use function preg_match;
use function sprintf;
use function str_replace;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_NO_AUTO_COMMIT;
/**
* OCI8 implementation of the Connection interface.
......@@ -52,7 +51,7 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
$password,
$db,
$charset = '',
$sessionMode = OCI_DEFAULT,
$sessionMode = OCI_NO_AUTO_COMMIT,
$persistent = false
) {
$dbh = $persistent
......@@ -101,17 +100,11 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
return false;
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : DriverStatement
{
return new OCI8Statement($this->dbh, $sql, $this);
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
$stmt = $this->prepare($sql);
......@@ -128,14 +121,12 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
if (is_int($value) || is_float($value)) {
return $value;
}
$value = str_replace("'", "''", $value);
return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
}
/**
* {@inheritdoc}
*/
public function exec(string $statement) : int
{
$stmt = $this->prepare($statement);
......@@ -192,6 +183,7 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
if (! oci_commit($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true;
......@@ -205,6 +197,7 @@ class OCI8Connection implements Connection, ServerInfoAwareConnection
if (! oci_rollback($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true;
......
......@@ -12,7 +12,7 @@ class OCI8Exception extends AbstractDriverException
/**
* @param mixed[]|false $error
*
* @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception
* @return OCI8Exception
*/
public static function fromErrorInfo($error)
{
......
......@@ -8,19 +8,6 @@ use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use IteratorAggregate;
use const OCI_ASSOC;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_BOTH;
use const OCI_D_LOB;
use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NUM;
use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS;
use const OCI_TEMP_BLOB;
use const PREG_OFFSET_CAPTURE;
use const SQLT_CHR;
use function array_key_exists;
use function assert;
use function count;
......@@ -41,6 +28,19 @@ use function preg_match;
use function preg_quote;
use function sprintf;
use function substr;
use const OCI_ASSOC;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_BOTH;
use const OCI_D_LOB;
use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NUM;
use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS;
use const OCI_TEMP_BLOB;
use const PREG_OFFSET_CAPTURE;
use const SQLT_CHR;
/**
* The OCI8 implementation of the Statement interface.
......@@ -518,9 +518,6 @@ class OCI8Statement implements IteratorAggregate, Statement
return $row[0] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
$count = oci_num_rows($this->_sth);
......
......@@ -34,9 +34,6 @@ class PDOConnection implements ServerInfoAwareConnection
}
}
/**
* {@inheritdoc}
*/
public function exec(string $statement) : int
{
try {
......@@ -54,9 +51,6 @@ class PDOConnection implements ServerInfoAwareConnection
return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : Statement
{
try {
......@@ -68,9 +62,6 @@ class PDOConnection implements ServerInfoAwareConnection
}
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
try {
......
......@@ -49,15 +49,19 @@ class Driver extends AbstractMySQLDriver
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
}
if (isset($params['unix_socket'])) {
$dsn .= 'unix_socket=' . $params['unix_socket'] . ';';
}
if (isset($params['charset'])) {
$dsn .= 'charset=' . $params['charset'] . ';';
}
......
......@@ -43,9 +43,6 @@ class Connection extends PDOConnection
return $val;
}
/**
* {@inheritDoc}
*/
protected function createStatement(\PDOStatement $stmt) : PDOStatement
{
return new Statement($stmt);
......
......@@ -104,16 +104,25 @@ class PDOStatement implements IteratorAggregate, Statement
}
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->stmt->columnCount();
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return $this->stmt->errorCode();
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return $this->stmt->errorInfo();
......
......@@ -104,9 +104,6 @@ class SQLAnywhereConnection implements ServerInfoAwareConnection
return sasql_error($this->connection);
}
/**
* {@inheritdoc}
*/
public function exec(string $statement) : int
{
if (sasql_real_query($this->connection, $statement) === false) {
......@@ -140,17 +137,11 @@ class SQLAnywhereConnection implements ServerInfoAwareConnection
return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : DriverStatement
{
return new SQLAnywhereStatement($this->connection, $sql);
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
$stmt = $this->prepare($sql);
......
......@@ -7,7 +7,6 @@ use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use const SASQL_BOTH;
use function array_key_exists;
use function assert;
use function is_int;
......@@ -24,6 +23,7 @@ use function sasql_stmt_execute;
use function sasql_stmt_field_count;
use function sasql_stmt_reset;
use function sasql_stmt_result_metadata;
use const SASQL_BOTH;
/**
* SAP SQL Anywhere implementation of the Statement interface.
......@@ -222,6 +222,7 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
}
break;
default:
......@@ -255,9 +256,6 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
return sasql_stmt_affected_rows($this->stmt);
......
......@@ -6,7 +6,6 @@ use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\Driver\Statement as DriverStatement;
use Doctrine\DBAL\ParameterType;
use const SQLSRV_ERR_ERRORS;
use function is_float;
use function is_int;
use function sprintf;
......@@ -20,6 +19,7 @@ use function sqlsrv_rollback;
use function sqlsrv_rows_affected;
use function sqlsrv_server_info;
use function str_replace;
use const SQLSRV_ERR_ERRORS;
/**
* SQL Server implementation for the Connection interface.
......@@ -72,17 +72,11 @@ class SQLSrvConnection implements ServerInfoAwareConnection
return false;
}
/**
* {@inheritDoc}
*/
public function prepare(string $sql) : DriverStatement
{
return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
}
/**
* {@inheritDoc}
*/
public function query(string $sql) : ResultStatement
{
$stmt = $this->prepare($sql);
......@@ -107,9 +101,6 @@ class SQLSrvConnection implements ServerInfoAwareConnection
return "'" . str_replace("'", "''", $value) . "'";
}
/**
* {@inheritDoc}
*/
public function exec(string $statement) : int
{
$stmt = sqlsrv_query($this->conn, $statement);
......
......@@ -3,9 +3,9 @@
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\AbstractDriverException;
use const SQLSRV_ERR_ERRORS;
use function rtrim;
use function sqlsrv_errors;
use const SQLSRV_ERR_ERRORS;
/**
* @psalm-immutable
......@@ -15,7 +15,7 @@ class SQLSrvException extends AbstractDriverException
/**
* Helper method to turn sql server errors into exception.
*
* @return \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException
* @return SQLSrvException
*/
public static function fromSqlSrvErrors()
{
......
......@@ -7,12 +7,6 @@ use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_ERR_ERRORS;
use const SQLSRV_FETCH_ASSOC;
use const SQLSRV_FETCH_BOTH;
use const SQLSRV_FETCH_NUMERIC;
use const SQLSRV_PARAM_IN;
use function array_key_exists;
use function is_int;
use function is_numeric;
......@@ -29,6 +23,12 @@ use function sqlsrv_prepare;
use function sqlsrv_rows_affected;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_ERR_ERRORS;
use const SQLSRV_FETCH_ASSOC;
use const SQLSRV_FETCH_BOTH;
use const SQLSRV_FETCH_NUMERIC;
use const SQLSRV_PARAM_IN;
/**
* SQL Server Statement.
......@@ -358,6 +358,7 @@ class SQLSrvStatement implements IteratorAggregate, Statement
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
}
break;
default:
......@@ -383,9 +384,6 @@ class SQLSrvStatement implements IteratorAggregate, Statement
return $row[0] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
if ($this->stmt === null) {
......
......@@ -127,6 +127,7 @@ final class DriverManager
if ($config === null) {
$config = new Configuration();
}
if ($eventManager === null) {
$eventManager = new EventManager();
}
......@@ -245,12 +246,15 @@ final class DriverManager
if (isset($url['host'])) {
$params['host'] = $url['host'];
}
if (isset($url['port'])) {
$params['port'] = $url['port'];
}
if (isset($url['user'])) {
$params['user'] = $url['user'];
}
if (isset($url['pass'])) {
$params['password'] = $url['pass'];
}
......
......@@ -5,11 +5,11 @@ namespace Doctrine\DBAL\Event\Listeners;
use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use const CASE_UPPER;
use function array_change_key_case;
use function array_merge;
use function count;
use function implode;
use const CASE_UPPER;
/**
* Should be used when Oracle Server default environment does not match the Doctrine requirements.
......@@ -57,6 +57,7 @@ class OracleSessionInit implements EventSubscriber
$vars[] = $option . " = '" . $value . "'";
}
}
$sql = 'ALTER SESSION SET ' . implode(' ', $vars);
$args->getConnection()->executeUpdate($sql);
}
......
......@@ -62,7 +62,7 @@ class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs
* @return SchemaAlterTableAddColumnEventArgs
*/
public function addSql($sql)
{
......
......@@ -62,7 +62,7 @@ class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs
* @return SchemaAlterTableChangeColumnEventArgs
*/
public function addSql($sql)
{
......
......@@ -49,7 +49,7 @@ class SchemaAlterTableEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs
* @return SchemaAlterTableEventArgs
*/
public function addSql($sql)
{
......
......@@ -62,7 +62,7 @@ class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs
* @return SchemaAlterTableRemoveColumnEventArgs
*/
public function addSql($sql)
{
......
......@@ -77,7 +77,7 @@ class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs
* @return SchemaAlterTableRenameColumnEventArgs
*/
public function addSql($sql)
{
......
......@@ -47,7 +47,7 @@ class SchemaColumnDefinitionEventArgs extends SchemaEventArgs
* Allows to clear the column which means the column will be excluded from
* tables column list.
*
* @return \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs
* @return SchemaColumnDefinitionEventArgs
*/
public function setColumn(?Column $column = null)
{
......
......@@ -62,7 +62,7 @@ class SchemaCreateTableColumnEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs
* @return SchemaCreateTableColumnEventArgs
*/
public function addSql($sql)
{
......
......@@ -77,7 +77,7 @@ class SchemaCreateTableEventArgs extends SchemaEventArgs
*
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs
* @return SchemaCreateTableEventArgs
*/
public function addSql($sql)
{
......
......@@ -50,7 +50,7 @@ class SchemaDropTableEventArgs extends SchemaEventArgs
/**
* @param string $sql
*
* @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs
* @return SchemaDropTableEventArgs
*/
public function setSql($sql)
{
......
......@@ -13,7 +13,7 @@ class SchemaEventArgs extends EventArgs
private $preventDefault = false;
/**
* @return \Doctrine\DBAL\Event\SchemaEventArgs
* @return SchemaEventArgs
*/
public function preventDefault()
{
......
......@@ -8,10 +8,10 @@ use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\LockMode;
use Throwable;
use const CASE_LOWER;
use function array_change_key_case;
use function assert;
use function is_int;
use const CASE_LOWER;
/**
* Table ID Generator for those poor languages that are missing sequences.
......@@ -72,6 +72,7 @@ class TableGenerator
if ($params['driver'] === 'pdo_sqlite') {
throw new DBALException('Cannot use TableGenerator with SQLite.');
}
$this->conn = DriverManager::getConnection($params, $conn->getConfiguration(), $conn->getEventManager());
$this->generatorTableName = $generatorTableName;
}
......@@ -141,6 +142,7 @@ class TableGenerator
$this->conn->commit();
} catch (Throwable $e) {
$this->conn->rollBack();
throw new DBALException('Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(), 0, $e);
}
......
<?php
namespace Doctrine\DBAL\Internal;
use function array_reverse;
/**
* DependencyOrderCalculator implements topological sorting, which is an ordering
* algorithm for directed graphs (DG) and/or directed acyclic graphs (DAG) by
* using a depth-first searching (DFS) to traverse the graph built in memory.
* This algorithm have a linear running time based on nodes (V) and dependency
* between the nodes (E), resulting in a computational complexity of O(V + E).
*/
final class DependencyOrderCalculator
{
public const NOT_VISITED = 0;
public const IN_PROGRESS = 1;
public const VISITED = 2;
/**
* Matrix of nodes (aka. vertex).
* Keys are provided hashes and values are the node definition objects.
*
* @var array<string,DependencyOrderNode>
*/
private $nodeList = [];
/**
* Volatile variable holding calculated nodes during sorting process.
*
* @var array<object>
*/
private $sortedNodeList = [];
/**
* Checks for node (vertex) existence in graph.
*/
public function hasNode(string $hash) : bool
{
return isset($this->nodeList[$hash]);
}
/**
* Adds a new node (vertex) to the graph, assigning its hash and value.
*
* @param object $node
*/
public function addNode(string $hash, $node) : void
{
$vertex = new DependencyOrderNode();
$vertex->hash = $hash;
$vertex->state = self::NOT_VISITED;
$vertex->value = $node;
$this->nodeList[$hash] = $vertex;
}
/**
* Adds a new dependency (edge) to the graph using their hashes.
*/
public function addDependency(string $fromHash, string $toHash) : void
{
$vertex = $this->nodeList[$fromHash];
$edge = new DependencyOrderEdge();
$edge->from = $fromHash;
$edge->to = $toHash;
$vertex->dependencyList[$toHash] = $edge;
}
/**
* Return a valid order list of all current nodes.
* The desired topological sorting is the reverse post order of these searches.
*
* {@internal Highly performance-sensitive method.}
*
* @return array<object>
*/
public function sort() : array
{
foreach ($this->nodeList as $vertex) {
if ($vertex->state !== self::NOT_VISITED) {
continue;
}
$this->visit($vertex);
}
$sortedList = $this->sortedNodeList;
$this->nodeList = [];
$this->sortedNodeList = [];
return array_reverse($sortedList);
}
/**
* Visit a given node definition for reordering.
*
* {@internal Highly performance-sensitive method.}
*/
private function visit(DependencyOrderNode $vertex) : void
{
$vertex->state = self::IN_PROGRESS;
foreach ($vertex->dependencyList as $edge) {
$adjacentVertex = $this->nodeList[$edge->to];
switch ($adjacentVertex->state) {
case self::VISITED:
case self::IN_PROGRESS:
// Do nothing, since node was already visited or is
// currently visited
break;
case self::NOT_VISITED:
$this->visit($adjacentVertex);
}
}
if ($vertex->state === self::VISITED) {
return;
}
$vertex->state = self::VISITED;
$this->sortedNodeList[] = $vertex->value;
}
}
<?php
namespace Doctrine\DBAL\Internal;
class DependencyOrderEdge
{
/** @var string */
public $from;
/** @var string */
public $to;
}
<?php
namespace Doctrine\DBAL\Internal;
class DependencyOrderNode
{
/** @var string */
public $hash;
/** @var int */
public $state;
/** @var object */
public $value;
/** @var DependencyOrderEdge[] */
public $dependencyList = [];
}
......@@ -28,7 +28,6 @@ use Doctrine\DBAL\Types;
use Doctrine\DBAL\Types\Type;
use InvalidArgumentException;
use UnexpectedValueException;
use const E_USER_DEPRECATED;
use function addcslashes;
use function array_map;
use function array_merge;
......@@ -55,6 +54,7 @@ use function strpos;
use function strtolower;
use function strtoupper;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Base class for all DatabasePlatforms. The DatabasePlatforms are the central
......@@ -1552,7 +1552,6 @@ abstract class AbstractPlatform
if (($createFlags&self::CREATE_INDEXES) > 0) {
foreach ($table->getIndexes() as $index) {
/** @var $index Index */
if ($index->isPrimary()) {
$options['primary'] = $index->getQuotedColumns($this);
$options['primary_index'] = $index;
......@@ -1614,6 +1613,7 @@ abstract class AbstractPlatform
if ($table->hasOption('comment')) {
$sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment'));
}
foreach ($table->getColumns() as $column) {
$comment = $this->getColumnComment($column);
......@@ -1712,6 +1712,7 @@ abstract class AbstractPlatform
if (! empty($check)) {
$query .= ', ' . $check;
}
$query .= ')';
$sql = [$query];
......@@ -1793,6 +1794,7 @@ abstract class AbstractPlatform
$referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) .
' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')';
}
$query .= ' ' . $columnList . $referencesClause;
return $query;
......@@ -1812,6 +1814,7 @@ abstract class AbstractPlatform
if ($table instanceof Table) {
$table = $table->getQuotedName($this);
}
$name = $index->getQuotedName($this);
$columns = $index->getColumns();
......@@ -2080,6 +2083,7 @@ abstract class AbstractPlatform
foreach ($diff->removedForeignKeys as $foreignKey) {
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
foreach ($diff->changedForeignKeys as $foreignKey) {
$sql[] = $this->getDropForeignKeySQL($foreignKey, $tableName);
}
......@@ -2088,6 +2092,7 @@ abstract class AbstractPlatform
foreach ($diff->removedIndexes as $index) {
$sql[] = $this->getDropIndexSQL($index, $tableName);
}
foreach ($diff->changedIndexes as $index) {
$sql[] = $this->getDropIndexSQL($index, $tableName);
}
......@@ -2514,6 +2519,7 @@ abstract class AbstractPlatform
if ($this->supportsForeignKeyOnUpdate() && $foreignKey->hasOption('onUpdate')) {
$query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate'));
}
if ($foreignKey->hasOption('onDelete')) {
$query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete'));
}
......@@ -2540,6 +2546,7 @@ abstract class AbstractPlatform
case 'RESTRICT':
case 'SET DEFAULT':
return $upper;
default:
throw new InvalidArgumentException('Invalid foreign key action: ' . $upper);
}
......@@ -2559,14 +2566,17 @@ abstract class AbstractPlatform
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.");
}
......@@ -2743,12 +2753,16 @@ abstract class AbstractPlatform
switch ($level) {
case TransactionIsolationLevel::READ_UNCOMMITTED:
return 'READ UNCOMMITTED';
case TransactionIsolationLevel::READ_COMMITTED:
return 'READ COMMITTED';
case TransactionIsolationLevel::REPEATABLE_READ:
return 'REPEATABLE READ';
case TransactionIsolationLevel::SERIALIZABLE:
return 'SERIALIZABLE';
default:
throw new InvalidArgumentException('Invalid isolation level:' . $level);
}
......@@ -3183,6 +3197,16 @@ abstract class AbstractPlatform
return true;
}
/**
* Whether foreign key constraints can be dropped.
*
* If false, then getDropForeignKeySQL() throws exception.
*/
public function supportsCreateDropForeignKeyConstraints() : bool
{
return true;
}
/**
* Whether this platform supports onUpdate in foreign key constraints.
*
......
......@@ -22,9 +22,6 @@ use function strtoupper;
class DB2Platform extends AbstractPlatform
{
/**
* {@inheritdoc}
*/
public function getCharMaxLength() : int
{
return 254;
......@@ -495,6 +492,7 @@ class DB2Platform extends AbstractPlatform
if (isset($options['indexes'])) {
$indexes = $options['indexes'];
}
$options['indexes'] = [];
$sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
......@@ -708,21 +706,23 @@ class DB2Platform extends AbstractPlatform
foreach ($diff->removedIndexes as $remKey => $remIndex) {
foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() === $addIndex->getColumns()) {
if ($remIndex->isPrimary()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
} elseif ($remIndex->isUnique()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
} else {
$sql[] = $this->getDropIndexSQL($remIndex, $table);
}
if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
$sql[] = $this->getCreateIndexSQL($addIndex, $table);
if ($remIndex->isPrimary()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
} elseif ($remIndex->isUnique()) {
$sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
} else {
$sql[] = $this->getDropIndexSQL($remIndex, $table);
}
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
$sql[] = $this->getCreateIndexSQL($addIndex, $table);
break;
}
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
}
......
......@@ -9,9 +9,6 @@ namespace Doctrine\DBAL\Platforms\Keywords;
*/
final class MariaDb102Keywords extends MySQLKeywords
{
/**
* {@inheritdoc}
*/
public function getName() : string
{
return 'MariaDb102';
......
......@@ -9,9 +9,6 @@ namespace Doctrine\DBAL\Platforms\Keywords;
*/
class PostgreSQL100Keywords extends PostgreSQL94Keywords
{
/**
* {@inheritdoc}
*/
public function getName() : string
{
return 'PostgreSQL100';
......
......@@ -21,17 +21,11 @@ final class MariaDb1027Platform extends MySqlPlatform
return 'LONGTEXT';
}
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass() : string
{
return Keywords\MariaDb102Keywords::class;
}
/**
* {@inheritdoc}
*/
protected function initializeDoctrineTypeMappings() : void
{
parent::initializeDoctrineTypeMappings();
......
......@@ -637,6 +637,7 @@ SQL
if (count($queryParts) > 0) {
$sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(', ', $queryParts);
}
$sql = array_merge(
$this->getPreAlterTableIndexForeignKeySQL($diff),
$sql,
......@@ -663,25 +664,27 @@ SQL
$sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $remIndex));
foreach ($diff->addedIndexes as $addKey => $addIndex) {
if ($remIndex->getColumns() === $addIndex->getColumns()) {
$indexClause = 'INDEX ' . $addIndex->getName();
if ($remIndex->getColumns() !== $addIndex->getColumns()) {
continue;
}
if ($addIndex->isPrimary()) {
$indexClause = 'PRIMARY KEY';
} elseif ($addIndex->isUnique()) {
$indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
}
$indexClause = 'INDEX ' . $addIndex->getName();
$query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
$query .= 'ADD ' . $indexClause;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')';
if ($addIndex->isPrimary()) {
$indexClause = 'PRIMARY KEY';
} elseif ($addIndex->isUnique()) {
$indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
}
$sql[] = $query;
$query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
$query .= 'ADD ' . $indexClause;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')';
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
$sql[] = $query;
break;
}
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
}
......@@ -987,6 +990,7 @@ SQL
if ($foreignKey->hasOption('match')) {
$query .= ' MATCH ' . $foreignKey->getOption('match');
}
$query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
return $query;
......@@ -1182,9 +1186,6 @@ SQL
return TransactionIsolationLevel::REPEATABLE_READ;
}
/**
* {@inheritdoc}
*/
public function supportsColumnLengthIndexes() : bool
{
return true;
......
......@@ -242,11 +242,14 @@ class OraclePlatform extends AbstractPlatform
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);
}
......
......@@ -11,14 +11,14 @@ use Doctrine\DBAL\Platforms\Keywords\PostgreSQL100Keywords;
*/
class PostgreSQL100Platform extends PostgreSQL94Platform
{
/**
* {@inheritdoc}
*/
protected function getReservedKeywordsClass() : string
{
return PostgreSQL100Keywords::class;
}
/**
* {@inheritDoc}
*/
public function getListSequencesSQL($database) : string
{
return 'SELECT sequence_name AS relname,
......
......@@ -530,7 +530,6 @@ SQL
}
foreach ($diff->changedColumns as $columnDiff) {
/** @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
continue;
}
......
......@@ -19,6 +19,7 @@ use UnexpectedValueException;
use function array_merge;
use function array_unique;
use function array_values;
use function assert;
use function count;
use function explode;
use function func_get_args;
......@@ -637,6 +638,7 @@ class SQLAnywhere16Platform extends AbstractPlatform
case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
return 'UNIQUE FULL';
default:
throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
}
......@@ -1136,8 +1138,10 @@ SQL
switch ($pos) {
case TrimMode::LEADING:
return $this->getLtrimExpression($str);
case TrimMode::TRAILING:
return $this->getRtrimExpression($str);
default:
return 'TRIM(' . $str . ')';
}
......@@ -1148,8 +1152,10 @@ SQL
switch ($pos) {
case TrimMode::LEADING:
return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
case TrimMode::TRAILING:
return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
default:
return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
......@@ -1325,8 +1331,8 @@ SQL
}
if (! empty($options['indexes'])) {
/** @var Index $index */
foreach ((array) $options['indexes'] as $index) {
assert($index instanceof Index);
$indexSql[] = $this->getCreateIndexSQL($index, $tableName);
}
}
......@@ -1367,12 +1373,16 @@ SQL
switch ($level) {
case TransactionIsolationLevel::READ_UNCOMMITTED:
return '0';
case TransactionIsolationLevel::READ_COMMITTED:
return '1';
case TransactionIsolationLevel::REPEATABLE_READ:
return '2';
case TransactionIsolationLevel::SERIALIZABLE:
return '3';
default:
throw new InvalidArgumentException('Invalid isolation level:' . $level);
}
......
......@@ -12,7 +12,6 @@ use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
use InvalidArgumentException;
use const PREG_OFFSET_CAPTURE;
use function array_merge;
use function array_unique;
use function array_values;
......@@ -33,6 +32,7 @@ use function str_replace;
use function strpos;
use function strtoupper;
use function substr_count;
use const PREG_OFFSET_CAPTURE;
/**
* Provides the behavior, features and SQL dialect of the Microsoft SQL Server 2012 database platform.
......@@ -143,26 +143,17 @@ class SQLServer2012Platform extends AbstractPlatform
return true;
}
/**
* {@inheritdoc}
*/
public function supportsSequences() : bool
{
return true;
}
/**
* {@inheritdoc}
*/
public function getAlterSequenceSQL(Sequence $sequence) : string
{
return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) .
' INCREMENT BY ' . $sequence->getAllocationSize();
}
/**
* {@inheritdoc}
*/
public function getCreateSequenceSQL(Sequence $sequence) : string
{
return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) .
......@@ -346,6 +337,7 @@ SQL
if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) {
$flags = ' NONCLUSTERED';
}
$columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')';
}
......@@ -355,6 +347,7 @@ SQL
if (! empty($check)) {
$query .= ', ' . $check;
}
$query .= ')';
$sql = [$query];
......@@ -1350,7 +1343,7 @@ SQL
$matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE);
$orderByPos = false;
if ($matchesCount > 0) {
$orderByPos = $matches[0][($matchesCount - 1)][1];
$orderByPos = $matches[0][$matchesCount - 1][1];
}
if ($orderByPos === false
......@@ -1631,9 +1624,6 @@ SQL
return $name . ' ' . $columnDef;
}
/**
* {@inheritdoc}
*/
protected function getLikeWildcardCharacters() : string
{
return parent::getLikeWildcardCharacters() . '[]^';
......@@ -1671,8 +1661,8 @@ SQL
{
return sprintf(
<<<'SQL'
EXEC sys.sp_addextendedproperty @name=N'MS_Description',
@value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo',
EXEC sys.sp_addextendedproperty @name=N'MS_Description',
@value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo',
@level1type=N'TABLE', @level1name=N%s
SQL
,
......
......@@ -63,8 +63,10 @@ class SqlitePlatform extends AbstractPlatform
switch ($type) {
case 'time':
return 'time(\'now\')';
case 'date':
return 'date(\'now\')';
case 'timestamp':
default:
return 'datetime(\'now\')';
......@@ -76,7 +78,7 @@ class SqlitePlatform extends AbstractPlatform
*/
public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
{
$trimChar = $char !== false ? (', ' . $char) : '';
$trimChar = $char !== false ? ', ' . $char : '';
switch ($pos) {
case TrimMode::LEADING:
......@@ -130,26 +132,25 @@ class SqlitePlatform extends AbstractPlatform
case DateIntervalUnit::MINUTE:
case DateIntervalUnit::HOUR:
return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
}
default:
switch ($unit) {
case DateIntervalUnit::WEEK:
$interval *= 7;
$unit = DateIntervalUnit::DAY;
break;
case DateIntervalUnit::QUARTER:
$interval *= 3;
$unit = DateIntervalUnit::MONTH;
break;
}
switch ($unit) {
case DateIntervalUnit::WEEK:
$interval *= 7;
$unit = DateIntervalUnit::DAY;
break;
if (! is_numeric($interval)) {
$interval = "' || " . $interval . " || '";
}
case DateIntervalUnit::QUARTER:
$interval *= 3;
$unit = DateIntervalUnit::MONTH;
break;
}
return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
if (! is_numeric($interval)) {
$interval = "' || " . $interval . " || '";
}
return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')";
}
/**
......@@ -168,10 +169,12 @@ class SqlitePlatform extends AbstractPlatform
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);
}
......@@ -768,6 +771,11 @@ class SqlitePlatform extends AbstractPlatform
* {@inheritDoc}
*/
public function supportsForeignKeyConstraints()
{
return true;
}
public function supportsCreateDropForeignKeyConstraints() : bool
{
return false;
}
......@@ -785,7 +793,7 @@ class SqlitePlatform extends AbstractPlatform
*/
public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table)
{
throw new DBALException('Sqlite platform does not support alter foreign key.');
throw new DBALException('Sqlite platform does not support alter foreign key, the table must be fully recreated using getAlterTableSQL.');
}
/**
......@@ -793,7 +801,7 @@ class SqlitePlatform extends AbstractPlatform
*/
public function getDropForeignKeySQL($foreignKey, $table)
{
throw new DBALException('Sqlite platform does not support alter foreign key.');
throw new DBALException('Sqlite platform does not support alter foreign key, the table must be fully recreated using getAlterTableSQL.');
}
/**
......
......@@ -60,6 +60,7 @@ class Connection extends \Doctrine\DBAL\Connection
} else {
$params['portability'] &= self::PORTABILITY_OTHERVENDORS;
}
$this->portability = $params['portability'];
}
......@@ -103,9 +104,6 @@ class Connection extends \Doctrine\DBAL\Connection
return $stmt;
}
/**
* {@inheritdoc}
*/
public function prepare(string $sql) : DriverStatement
{
$stmt = new Statement(parent::prepare($sql), $this);
......@@ -114,9 +112,6 @@ class Connection extends \Doctrine\DBAL\Connection
return $stmt;
}
/**
* {@inheritdoc}
*/
public function query(string $sql) : ResultStatement
{
$connection = $this->getWrappedConnection();
......
......@@ -228,9 +228,6 @@ class Statement implements IteratorAggregate, DriverStatement
return $value;
}
/**
* {@inheritdoc}
*/
public function rowCount() : int
{
assert($this->stmt instanceof DriverStatement);
......
......@@ -74,7 +74,7 @@ class CompositeExpression implements Countable
*
* @param self[]|string[] $parts
*
* @return \Doctrine\DBAL\Query\Expression\CompositeExpression
* @return CompositeExpression
*/
public function addMultiple(array $parts = [])
{
......@@ -92,7 +92,7 @@ class CompositeExpression implements Countable
*
* @param mixed $part
*
* @return \Doctrine\DBAL\Query\Expression\CompositeExpression
* @return CompositeExpression
*/
public function add($part)
{
......
......@@ -232,6 +232,7 @@ class QueryBuilder
case self::INSERT:
$sql = $this->getSQLForInsert();
break;
case self::DELETE:
$sql = $this->getSQLForDelete();
break;
......@@ -1287,6 +1288,7 @@ class QueryBuilder
$this->boundCounter++;
$placeHolder = ':dcValue' . $this->boundCounter;
}
$this->setParameter(substr($placeHolder, 1), $value, $type);
return $placeHolder;
......@@ -1339,11 +1341,13 @@ class QueryBuilder
if (array_key_exists($join['joinAlias'], $knownAliases)) {
throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
}
$sql .= ' ' . strtoupper($join['joinType'])
. ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'];
if ($join['joinCondition'] !== null) {
$sql .= ' ON ' . $join['joinCondition'];
}
$knownAliases[$join['joinAlias']] = true;
}
......
......@@ -14,7 +14,7 @@ class QueryException extends DBALException
* @param string $alias
* @param string[] $registeredAliases
*
* @return \Doctrine\DBAL\Query\QueryException
* @return QueryException
*/
public static function unknownAlias($alias, $registeredAliases)
{
......@@ -27,7 +27,7 @@ class QueryException extends DBALException
* @param string $alias
* @param string[] $registeredAliases
*
* @return \Doctrine\DBAL\Query\QueryException
* @return QueryException
*/
public static function nonUniqueAlias($alias, $registeredAliases)
{
......
......@@ -2,7 +2,6 @@
namespace Doctrine\DBAL;
use const PREG_OFFSET_CAPTURE;
use function array_fill;
use function array_fill_keys;
use function array_key_exists;
......@@ -20,6 +19,7 @@ use function sprintf;
use function strlen;
use function strpos;
use function substr;
use const PREG_OFFSET_CAPTURE;
/**
* Utility class that parses sql statements with regard to types and parameters.
......@@ -195,8 +195,8 @@ class SQLParserUtils
$expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL';
$query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1);
$paramOffset += ($count - 1); // Grows larger by number of parameters minus the replaced needle.
$queryOffset += (strlen($expandStr) - 1);
$paramOffset += $count - 1; // Grows larger by number of parameters minus the replaced needle.
$queryOffset += strlen($expandStr) - 1;
}
return [$query, $params, $types];
......@@ -214,10 +214,10 @@ class SQLParserUtils
if (! isset($arrayPositions[$paramName]) && ! isset($arrayPositions[':' . $paramName])) {
$pos += $queryOffset;
$queryOffset -= ($paramLen - 1);
$queryOffset -= $paramLen - 1;
$paramsOrd[] = $value;
$typesOrd[] = static::extractParam($paramName, $types, false, ParameterType::STRING);
$query = substr($query, 0, $pos) . '?' . substr($query, ($pos + $paramLen));
$query = substr($query, 0, $pos) . '?' . substr($query, $pos + $paramLen);
continue;
}
......@@ -231,8 +231,8 @@ class SQLParserUtils
}
$pos += $queryOffset;
$queryOffset += (strlen($expandStr) - $paramLen);
$query = substr($query, 0, $pos) . $expandStr . substr($query, ($pos + $paramLen));
$queryOffset += strlen($expandStr) - $paramLen;
$query = substr($query, 0, $pos) . $expandStr . substr($query, $pos + $paramLen);
}
return [$query, $paramsOrd, $typesOrd];
......
......@@ -14,7 +14,7 @@ class SQLParserUtilsException extends DBALException
/**
* @param string $paramName
*
* @return \Doctrine\DBAL\SQLParserUtilsException
* @return SQLParserUtilsException
*/
public static function missingParam($paramName)
{
......@@ -24,7 +24,7 @@ class SQLParserUtilsException extends DBALException
/**
* @param string $typeName
*
* @return \Doctrine\DBAL\SQLParserUtilsException
* @return SQLParserUtilsException
*/
public static function missingType($typeName)
{
......
......@@ -48,11 +48,13 @@ abstract class AbstractAsset
$this->_quoted = true;
$name = $this->trimQuotes($name);
}
if (strpos($name, '.') !== false) {
$parts = explode('.', $name);
$this->_namespace = $parts[0];
$name = $parts[1];
}
$this->_name = $name;
}
......
......@@ -131,6 +131,7 @@ abstract class AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListSequencesSQL($database);
$sequences = $this->_conn->fetchAll($sql);
......@@ -272,6 +273,7 @@ abstract class AbstractSchemaManager
if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $this->listTableForeignKeys($tableName);
}
$indexes = $this->listTableIndexes($tableName);
return new Table($tableName, $columns, $indexes, $foreignKeys);
......@@ -304,6 +306,7 @@ abstract class AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
$tableForeignKeys = $this->_conn->fetchAll($sql);
......@@ -836,6 +839,7 @@ abstract class AbstractSchemaManager
if ($tableIndex['primary']) {
$keyName = 'primary';
}
$keyName = strtolower($keyName);
if (! isset($result[$keyName])) {
......@@ -1053,9 +1057,11 @@ abstract class AbstractSchemaManager
if (! isset($params['defaultTableOptions'])) {
$params['defaultTableOptions'] = [];
}
if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
$params['defaultTableOptions']['charset'] = $params['charset'];
}
$schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
return $schemaConfig;
......
......@@ -3,12 +3,12 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Types\Type;
use const E_USER_DEPRECATED;
use function array_merge;
use function is_numeric;
use function method_exists;
use function sprintf;
use function trigger_error;
use const E_USER_DEPRECATED;
/**
* Object representation of a database column.
......@@ -86,6 +86,7 @@ class Column extends AbstractAsset
continue;
}
$this->$method($value);
}
......
......@@ -89,6 +89,7 @@ class Comparator
if (! isset($foreignKeysToTable[$foreignTable])) {
$foreignKeysToTable[$foreignTable] = [];
}
$foreignKeysToTable[$foreignTable][] = $foreignKey;
}
}
......@@ -116,6 +117,7 @@ class Comparator
if ($tableName !== strtolower($removedForeignKey->getForeignTableName())) {
continue;
}
unset($diff->changedTables[$localTableName]->removedForeignKeys[$key]);
}
}
......@@ -205,6 +207,7 @@ class Comparator
$tableDifferences->addedColumns[$columnName] = $column;
$changes++;
}
/* See if there are any removed fields in table 2 */
foreach ($table1Columns as $columnName => $column) {
// See if column is removed in table 2.
......@@ -241,6 +244,7 @@ class Comparator
$tableDifferences->addedIndexes[$indexName] = $index;
$changes++;
}
/* See if there are any removed indexes in table 2 */
foreach ($table1Indexes as $indexName => $index) {
// See if index is removed in table 2.
......@@ -465,6 +469,7 @@ class Comparator
if (($properties1['precision'] ?? 10) !== ($properties2['precision'] ?? 10)) {
$changedProperties[] = 'precision';
}
if ($properties1['scale'] !== $properties2['scale']) {
$changedProperties[] = 'scale';
}
......
......@@ -4,13 +4,14 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function assert;
use function preg_match;
use function str_replace;
use function strpos;
use function strtolower;
use function substr;
use const CASE_LOWER;
/**
* IBM Db2 Schema Manager.
......@@ -67,13 +68,16 @@ class DB2SchemaManager extends AbstractSchemaManager
$length = $tableColumn['length'];
$fixed = false;
break;
case 'character':
$length = $tableColumn['length'];
$fixed = true;
break;
case 'clob':
$length = $tableColumn['length'];
break;
case 'decimal':
case 'double':
case 'real':
......@@ -212,13 +216,16 @@ class DB2SchemaManager extends AbstractSchemaManager
return new View($view['name'], $sql);
}
/**
* {@inheritdoc}
*/
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var DB2Platform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableCommentsSQL($tableName);
assert($platform instanceof DB2Platform);
$sql = $platform->getListTableCommentsSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
......
......@@ -65,6 +65,7 @@ class Index extends AbstractAsset implements Constraint
foreach ($columns as $column) {
$this->_addColumn($column);
}
foreach ($flags as $flag) {
$this->addFlag($flag);
}
......
......@@ -5,7 +5,6 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function array_shift;
use function array_values;
......@@ -17,6 +16,7 @@ use function strpos;
use function strtok;
use function strtolower;
use function strtr;
use const CASE_LOWER;
/**
* Schema manager for the MySql RDBMS.
......@@ -82,11 +82,13 @@ class MySqlSchemaManager extends AbstractSchemaManager
} else {
$v['primary'] = false;
}
if (strpos($v['index_type'], 'FULLTEXT') !== false) {
$v['flags'] = ['FULLTEXT'];
} elseif (strpos($v['index_type'], 'SPATIAL') !== false) {
$v['flags'] = ['SPATIAL'];
}
$v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null;
$tableIndexes[$k] = $v;
......@@ -138,6 +140,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
case 'binary':
$fixed = true;
break;
case 'float':
case 'double':
case 'real':
......@@ -152,25 +155,33 @@ class MySqlSchemaManager extends AbstractSchemaManager
$scale = $match[2];
$length = null;
}
break;
case 'tinytext':
$length = MySqlPlatform::LENGTH_LIMIT_TINYTEXT;
break;
case 'text':
$length = MySqlPlatform::LENGTH_LIMIT_TEXT;
break;
case 'mediumtext':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMTEXT;
break;
case 'tinyblob':
$length = MySqlPlatform::LENGTH_LIMIT_TINYBLOB;
break;
case 'blob':
$length = MySqlPlatform::LENGTH_LIMIT_BLOB;
break;
case 'mediumblob':
$length = MySqlPlatform::LENGTH_LIMIT_MEDIUMBLOB;
break;
case 'tinyint':
case 'smallint':
case 'mediumint':
......@@ -212,6 +223,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
if (isset($tableColumn['characterset'])) {
$column->setPlatformOption('charset', $tableColumn['characterset']);
}
if (isset($tableColumn['collation'])) {
$column->setPlatformOption('collation', $tableColumn['collation']);
}
......@@ -248,8 +260,10 @@ class MySqlSchemaManager extends AbstractSchemaManager
switch ($columnDefault) {
case 'current_timestamp()':
return $platform->getCurrentTimestampSQL();
case 'curdate()':
return $platform->getCurrentDateSQL();
case 'curtime()':
return $platform->getCurrentTimeSQL();
}
......@@ -269,6 +283,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') {
$value['delete_rule'] = null;
}
if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') {
$value['update_rule'] = null;
}
......@@ -282,6 +297,7 @@ class MySqlSchemaManager extends AbstractSchemaManager
'onUpdate' => $value['update_rule'],
];
}
$list[$value['constraint_name']]['local'][] = $value['column_name'];
$list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name'];
}
......@@ -303,13 +319,16 @@ class MySqlSchemaManager extends AbstractSchemaManager
return $result;
}
/**
* {@inheritdoc}
*/
public function listTableDetails($tableName)
{
$table = parent::listTableDetails($tableName);
/** @var MySqlPlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
assert($platform instanceof MySqlPlatform);
$sql = $platform->getListTableMetadataSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
......
......@@ -7,7 +7,6 @@ use Doctrine\DBAL\Driver\DriverException;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Types\Type;
use Throwable;
use const CASE_LOWER;
use function array_change_key_case;
use function array_values;
use function assert;
......@@ -18,6 +17,7 @@ use function strpos;
use function strtolower;
use function strtoupper;
use function trim;
use const CASE_LOWER;
/**
* Oracle Schema Manager.
......@@ -107,6 +107,7 @@ class OracleSchemaManager extends AbstractSchemaManager
$buffer['primary'] = false;
$buffer['non_unique'] = ! $tableIndex['is_unique'];
}
$buffer['key_name'] = $keyName;
$buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']);
$indexBuffer[] = $buffer;
......@@ -176,12 +177,14 @@ class OracleSchemaManager extends AbstractSchemaManager
}
break;
case 'varchar':
case 'varchar2':
case 'nvarchar2':
$length = $tableColumn['char_length'];
$fixed = false;
break;
case 'char':
case 'nchar':
$length = $tableColumn['char_length'];
......@@ -392,13 +395,16 @@ SQL;
}
}
/**
* {@inheritdoc}
*/
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var OraclePlatform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableCommentsSQL($tableName);
assert($platform instanceof OraclePlatform);
$sql = $platform->getListTableCommentsSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
......
......@@ -7,7 +7,6 @@ use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use const CASE_LOWER;
use function array_change_key_case;
use function array_filter;
use function array_keys;
......@@ -25,6 +24,7 @@ use function strlen;
use function strpos;
use function strtolower;
use function trim;
use const CASE_LOWER;
/**
* PostgreSQL Schema Manager.
......@@ -145,6 +145,7 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
) === 1) {
$onUpdate = $match[1];
}
if (preg_match(
'(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))',
$tableForeignKey['condef'],
......@@ -351,9 +352,11 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
if ($length === '-1' && isset($tableColumn['atttypmod'])) {
$length = $tableColumn['atttypmod'] - 4;
}
if ((int) $length <= 0) {
$length = null;
}
$fixed = null;
if (! isset($tableColumn['name'])) {
......@@ -381,17 +384,20 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$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;
break;
case 'bool':
case 'boolean':
if ($tableColumn['default'] === 'true') {
......@@ -404,6 +410,7 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$length = null;
break;
case 'text':
case '_varchar':
case 'varchar':
......@@ -413,10 +420,12 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
case 'interval':
$fixed = false;
break;
case 'char':
case 'bpchar':
$fixed = true;
break;
case 'float':
case 'float4':
case 'float8':
......@@ -437,7 +446,9 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
$scale = $match[2];
$length = null;
}
break;
case 'year':
$length = null;
break;
......@@ -511,13 +522,16 @@ class PostgreSqlSchemaManager extends AbstractSchemaManager
return str_replace("''", "'", $default);
}
/**
* {@inheritdoc}
*/
public function listTableDetails($tableName) : Table
{
$table = parent::listTableDetails($tableName);
/** @var PostgreSQL94Platform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
assert($platform instanceof PostgreSQL94Platform);
$sql = $platform->getListTableMetadataSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
......
......@@ -112,6 +112,7 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
case 'char':
case 'nchar':
$fixed = true;
break;
}
switch ($type) {
......@@ -119,6 +120,7 @@ class SQLAnywhereSchemaManager extends AbstractSchemaManager
case 'float':
$precision = $tableColumn['length'];
$scale = $tableColumn['scale'];
break;
}
return new Column(
......
......@@ -86,11 +86,13 @@ class SQLServerSchemaManager extends AbstractSchemaManager
// Unicode data requires 2 bytes per character
$length /= 2;
break;
case 'varchar':
// TEXT type is returned as VARCHAR(MAX) with a length of -1
if ($length === -1) {
$dbType = 'text';
}
break;
}
......@@ -336,9 +338,9 @@ class SQLServerSchemaManager extends AbstractSchemaManager
{
$table = parent::listTableDetails($tableName);
/** @var SQLServer2012Platform $platform */
$platform = $this->_platform;
$sql = $platform->getListTableMetadataSQL($tableName);
assert($platform instanceof SQLServer2012Platform);
$sql = $platform->getListTableMetadataSQL($tableName);
$tableOptions = $this->_conn->fetchAssoc($sql);
......
......@@ -67,6 +67,7 @@ class Schema extends AbstractAsset
if ($schemaConfig === null) {
$schemaConfig = new SchemaConfig();
}
$this->_schemaConfig = $schemaConfig;
$this->_setName($schemaConfig->getName() ?? 'public');
......@@ -287,7 +288,7 @@ class Schema extends AbstractAsset
*
* @param string $namespaceName The name of the namespace to create.
*
* @return \Doctrine\DBAL\Schema\Schema This schema instance.
* @return Schema This schema instance.
*
* @throws SchemaException
*/
......@@ -329,7 +330,7 @@ class Schema extends AbstractAsset
* @param string $oldTableName
* @param string $newTableName
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function renameTable($oldTableName, $newTableName)
{
......@@ -347,7 +348,7 @@ class Schema extends AbstractAsset
*
* @param string $tableName
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function dropTable($tableName)
{
......@@ -378,7 +379,7 @@ class Schema extends AbstractAsset
/**
* @param string $sequenceName
*
* @return \Doctrine\DBAL\Schema\Schema
* @return Schema
*/
public function dropSequence($sequenceName)
{
......@@ -468,6 +469,7 @@ class Schema extends AbstractAsset
foreach ($this->_tables as $k => $table) {
$this->_tables[$k] = clone $table;
}
foreach ($this->_sequences as $k => $sequence) {
$this->_sequences[$k] = clone $sequence;
}
......
......@@ -2,6 +2,7 @@
namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\Internal\DependencyOrderCalculator;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_merge;
......@@ -137,13 +138,16 @@ class SchemaDiff
}
$foreignKeySql = [];
foreach ($this->newTables as $table) {
$sql = array_merge(
$sql,
$platform->getCreateTableSQL($table, AbstractPlatform::CREATE_INDEXES)
);
$createFlags = AbstractPlatform::CREATE_INDEXES;
if (! $platform->supportsCreateDropForeignKeyConstraints()) {
$createFlags |= AbstractPlatform::CREATE_FOREIGNKEYS;
}
if (! $platform->supportsForeignKeyConstraints()) {
foreach ($this->getNewTablesSortedByDependencies() as $table) {
$sql = array_merge($sql, $platform->getCreateTableSQL($table, $createFlags));
if (! $platform->supportsCreateDropForeignKeyConstraints()) {
continue;
}
......@@ -151,6 +155,7 @@ class SchemaDiff
$foreignKeySql[] = $platform->getCreateForeignKeySQL($foreignKey, $table);
}
}
$sql = array_merge($sql, $foreignKeySql);
if ($saveMode === false) {
......@@ -165,4 +170,37 @@ class SchemaDiff
return $sql;
}
/**
* Sorts tables by dependencies so that they are created in the right order.
*
* This is necessary when one table depends on another while creating foreign key
* constraints directly during CREATE TABLE.
*
* @return array<Table>
*/
private function getNewTablesSortedByDependencies()
{
$calculator = new DependencyOrderCalculator();
$newTables = [];
foreach ($this->newTables as $table) {
$newTables[$table->getName()] = true;
$calculator->addNode($table->getName(), $table);
}
foreach ($this->newTables as $table) {
foreach ($table->getForeignKeys() as $foreignKey) {
$foreignTableName = $foreignKey->getForeignTableName();
if (! isset($newTables[$foreignTableName])) {
continue;
}
$calculator->addDependency($foreignTableName, $table->getName());
}
}
return $calculator->sort();
}
}
......@@ -26,7 +26,7 @@ class SchemaException extends DBALException
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function tableDoesNotExist($tableName)
{
......@@ -36,7 +36,7 @@ class SchemaException extends DBALException
/**
* @param string $indexName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexNameInvalid($indexName)
{
......@@ -50,7 +50,7 @@ class SchemaException extends DBALException
* @param string $indexName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexDoesNotExist($indexName, $table)
{
......@@ -64,7 +64,7 @@ class SchemaException extends DBALException
* @param string $indexName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function indexAlreadyExists($indexName, $table)
{
......@@ -78,7 +78,7 @@ class SchemaException extends DBALException
* @param string $columnName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function columnDoesNotExist($columnName, $table)
{
......@@ -91,7 +91,7 @@ class SchemaException extends DBALException
/**
* @param string $namespaceName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function namespaceAlreadyExists($namespaceName)
{
......@@ -104,7 +104,7 @@ class SchemaException extends DBALException
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function tableAlreadyExists($tableName)
{
......@@ -115,7 +115,7 @@ class SchemaException extends DBALException
* @param string $tableName
* @param string $columnName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function columnAlreadyExists($tableName, $columnName)
{
......@@ -128,7 +128,7 @@ class SchemaException extends DBALException
/**
* @param string $sequenceName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function sequenceAlreadyExists($sequenceName)
{
......@@ -138,7 +138,7 @@ class SchemaException extends DBALException
/**
* @param string $sequenceName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function sequenceDoesNotExist($sequenceName)
{
......@@ -149,7 +149,7 @@ class SchemaException extends DBALException
* @param string $fkName
* @param string $table
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function foreignKeyDoesNotExist($fkName, $table)
{
......@@ -160,7 +160,7 @@ class SchemaException extends DBALException
}
/**
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function namedForeignKeyRequired(Table $localTable, ForeignKeyConstraint $foreignKey)
{
......@@ -175,7 +175,7 @@ class SchemaException extends DBALException
/**
* @param string $changeName
*
* @return \Doctrine\DBAL\Schema\SchemaException
* @return SchemaException
*/
public static function alterTableChangeNotSupported($changeName)
{
......
......@@ -61,7 +61,7 @@ class Sequence extends AbstractAsset
/**
* @param int $allocationSize
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setAllocationSize($allocationSize)
{
......@@ -77,7 +77,7 @@ class Sequence extends AbstractAsset
/**
* @param int $initialValue
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setInitialValue($initialValue)
{
......@@ -93,7 +93,7 @@ class Sequence extends AbstractAsset
/**
* @param int $cache
*
* @return \Doctrine\DBAL\Schema\Sequence
* @return Sequence
*/
public function setCache($cache)
{
......
......@@ -8,7 +8,6 @@ use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Types\StringType;
use Doctrine\DBAL\Types\TextType;
use Doctrine\DBAL\Types\Type;
use const CASE_LOWER;
use function array_change_key_case;
use function array_map;
use function array_reverse;
......@@ -27,6 +26,7 @@ use function strtolower;
use function trim;
use function unlink;
use function usort;
use const CASE_LOWER;
/**
* Sqlite SchemaManager.
......@@ -113,6 +113,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
if ($database === null) {
$database = $this->_conn->getDatabase();
}
$sql = $this->_platform->getListTableForeignKeysSQL($table, $database);
$tableForeignKeys = $this->_conn->fetchAll($sql);
......@@ -361,8 +362,10 @@ class SqliteSchemaManager extends AbstractSchemaManager
if (strpos($tableColumn['length'], ',') === false) {
$tableColumn['length'] .= ',0';
}
[$precision, $scale] = array_map('trim', explode(',', $tableColumn['length']));
}
$length = null;
break;
}
......@@ -402,6 +405,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
if (! isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') {
$value['on_delete'] = null;
}
if (! isset($value['on_update']) || $value['on_update'] === 'RESTRICT') {
$value['on_update'] = null;
}
......@@ -417,6 +421,7 @@ class SqliteSchemaManager extends AbstractSchemaManager
'deferred'=> $value['deferred'],
];
}
$list[$name]['local'][] = $value['from'];
$list[$name]['foreign'][] = $value['to'];
}
......
......@@ -5,13 +5,13 @@ namespace Doctrine\DBAL\Schema;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Visitor\Visitor;
use Doctrine\DBAL\Types\Type;
use const ARRAY_FILTER_USE_KEY;
use function array_filter;
use function array_merge;
use function in_array;
use function preg_match;
use function strlen;
use function strtolower;
use const ARRAY_FILTER_USE_KEY;
/**
* Object Representation of a table.
......@@ -169,6 +169,7 @@ class Table extends AbstractAsset
if (! $this->hasIndex($indexName)) {
throw SchemaException::indexDoesNotExist($indexName, $this->_name);
}
unset($this->_indexes[$indexName]);
}
......@@ -248,7 +249,6 @@ class Table extends AbstractAsset
public function columnsAreIndexed(array $columnNames)
{
foreach ($this->getIndexes() as $index) {
/** @var $index Index */
if ($index->spansColumns($columnNames)) {
return true;
}
......@@ -520,6 +520,7 @@ class Table extends AbstractAsset
$this->_getMaxIdentifierLength()
);
}
$name = $this->normalizeIdentifier($name);
$this->_fkConstraints[$name] = $constraint;
......@@ -826,9 +827,11 @@ class Table extends AbstractAsset
foreach ($this->_columns as $k => $column) {
$this->_columns[$k] = clone $column;
}
foreach ($this->_indexes as $k => $index) {
$this->_indexes[$k] = clone $index;
}
foreach ($this->_fkConstraints as $k => $fk) {
$this->_fkConstraints[$k] = clone $fk;
$this->_fkConstraints[$k]->setLocalTable($this);
......
......@@ -8,6 +8,7 @@ use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
use SplObjectStorage;
use function assert;
use function strlen;
/**
......@@ -46,6 +47,10 @@ class DropSchemaSqlCollector extends AbstractVisitor
*/
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint)
{
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
return;
}
if (strlen($fkConstraint->getName()) === 0) {
throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint);
}
......@@ -78,19 +83,19 @@ class DropSchemaSqlCollector extends AbstractVisitor
{
$sql = [];
/** @var ForeignKeyConstraint $fkConstraint */
foreach ($this->constraints as $fkConstraint) {
assert($fkConstraint instanceof ForeignKeyConstraint);
$localTable = $this->constraints[$fkConstraint];
$sql[] = $this->platform->getDropForeignKeySQL($fkConstraint, $localTable);
}
/** @var Sequence $sequence */
foreach ($this->sequences as $sequence) {
assert($sequence instanceof Sequence);
$sql[] = $this->platform->getDropSequenceSQL($sequence);
}
/** @var Table $table */
foreach ($this->tables as $table) {
assert($table instanceof Table);
$sql[] = $this->platform->getDropTableSQL($table);
}
......
......@@ -87,6 +87,7 @@ class Graphviz extends AbstractVisitor
if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns(), true)) {
$label .= "\xe2\x9c\xb7";
}
$label .= '</TD></TR>';
}
......@@ -108,6 +109,7 @@ class Graphviz extends AbstractVisitor
foreach ($options as $key => $value) {
$node .= $key . '=' . $value . ' ';
}
$node .= "]\n";
return $node;
......@@ -126,6 +128,7 @@ class Graphviz extends AbstractVisitor
foreach ($options as $key => $value) {
$relation .= $key . '=' . $value . ' ';
}
$relation .= "]\n";
return $relation;
......
......@@ -93,6 +93,7 @@ class Statement implements IteratorAggregate, DriverStatement
if (is_string($type)) {
$type = Type::getType($type);
}
if ($type instanceof Type) {
$value = $type->convertToDatabaseValue($value, $this->platform);
$bindingType = $type->getBindingType();
......@@ -153,6 +154,7 @@ class Statement implements IteratorAggregate, DriverStatement
if ($logger !== null) {
$logger->stopQuery();
}
throw DBALException::driverExceptionDuringQuery(
$this->conn->getDriver(),
$ex,
......@@ -164,6 +166,7 @@ class Statement implements IteratorAggregate, DriverStatement
if ($logger !== null) {
$logger->stopQuery();
}
$this->params = [];
$this->types = [];
......
......@@ -21,6 +21,7 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use function array_keys;
use function assert;
use function count;
use function implode;
......@@ -104,8 +105,8 @@ EOT
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
/** @var Connection $conn */
$conn = $this->getHelper('db')->getConnection();
assert($conn instanceof Connection);
$keywordLists = (array) $input->getOption('list');
if (count($keywordLists) === 0) {
......@@ -120,6 +121,7 @@ EOT
'Known lists: ' . implode(', ', array_keys($this->keywordListClasses))
);
}
$class = $this->keywordListClasses[$keywordList];
$keywords[] = new $class();
}
......
......@@ -150,6 +150,7 @@ final class Dumper
if ($aux[0] === '') {
$name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private');
}
$return->$name = self::export($clone[$key], $maxDepth - 1);
}
......
......@@ -26,7 +26,7 @@ class ConversionException extends DBALException
* @param string $value
* @param string $toType
*
* @return \Doctrine\DBAL\Types\ConversionException
* @return ConversionException
*/
public static function conversionFailed($value, $toType, ?Throwable $previous = null)
{
......@@ -43,7 +43,7 @@ class ConversionException extends DBALException
* @param string $toType
* @param string $expectedFormat
*
* @return \Doctrine\DBAL\Types\ConversionException
* @return ConversionException
*/
public static function conversionFailedFormat($value, $toType, $expectedFormat, ?Throwable $previous = null)
{
......@@ -64,7 +64,7 @@ class ConversionException extends DBALException
* @param string $toType
* @param string[] $possibleTypes
*
* @return \Doctrine\DBAL\Types\ConversionException
* @return ConversionException
*/
public static function conversionFailedInvalidType(
$value,
......
......@@ -3,13 +3,13 @@
namespace Doctrine\DBAL\Types;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use const JSON_ERROR_NONE;
use function is_resource;
use function json_decode;
use function json_encode;
use function json_last_error;
use function json_last_error_msg;
use function stream_get_contents;
use const JSON_ERROR_NONE;
/**
* Type generating json objects values
......
......@@ -222,7 +222,7 @@ abstract class Type
*
* @param string $name The name of the type (as returned by getName()).
*
* @return \Doctrine\DBAL\Types\Type
* @return Type
*
* @throws DBALException
*/
......
......@@ -17,9 +17,6 @@ class ConfigurationTest extends TestCase
*/
protected $config;
/**
* {@inheritdoc}
*/
protected function setUp() : void
{
$this->config = new Configuration();
......
......@@ -163,13 +163,11 @@ class ConnectionTest extends TestCase
{
$eventManager = new EventManager();
/** @var AbstractPlatform|MockObject $driver */
$platform = $this->createMock(AbstractPlatform::class);
$platform->expects(self::once())
->method('setEventManager')
->with($eventManager);
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class);
$driver->expects(self::any())
->method('getDatabasePlatform')
......@@ -572,7 +570,6 @@ class ConnectionTest extends TestCase
->with(FetchMode::ASSOCIATIVE)
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -608,7 +605,6 @@ class ConnectionTest extends TestCase
->with(FetchMode::NUMERIC)
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -643,7 +639,6 @@ class ConnectionTest extends TestCase
->method('fetchColumn')
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -678,7 +673,6 @@ class ConnectionTest extends TestCase
->method('fetchAll')
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -694,7 +688,6 @@ class ConnectionTest extends TestCase
public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$conn = new Connection([], $driver);
......@@ -704,7 +697,6 @@ class ConnectionTest extends TestCase
public function testCallConnectOnce() : void
{
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class);
$driver->expects(self::once())
->method('connect');
......@@ -721,13 +713,10 @@ class ConnectionTest extends TestCase
*/
public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
{
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class);
/** @var ServerInfoAwareConnection|MockObject $driverConnectionMock */
$driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
/** @var AbstractPlatform|MockObject $platformMock */
$platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
$connection = new Connection([], $driverMock);
......@@ -766,7 +755,6 @@ class ConnectionTest extends TestCase
$params = [666];
$types = [ParameterType::INTEGER];
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock
......@@ -781,7 +769,6 @@ class ConnectionTest extends TestCase
->with($query, $params, $types, $this->params)
->will(self::returnValue(['cacheKey', 'realKey']));
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
self::assertInstanceOf(
......@@ -803,7 +790,6 @@ class ConnectionTest extends TestCase
->with('cacheKey')
->will(self::returnValue(['realKey' => []]));
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock
......@@ -823,7 +809,6 @@ class ConnectionTest extends TestCase
$connectionParams['platform'] = $this->createMock(AbstractPlatform::class);
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
(new Connection($connectionParams, $driver))->executeCacheQuery($query, [], [], $queryCacheProfileMock);
......@@ -837,7 +822,6 @@ class ConnectionTest extends TestCase
$connectionParams = $this->params;
$connectionParams['platform'] = new stdClass();
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$this->expectException(DBALException::class);
......@@ -850,7 +834,6 @@ class ConnectionTest extends TestCase
*/
public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
{
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class);
$connection = new Connection(['dbname' => 'foo'], $driverMock);
......@@ -875,16 +858,12 @@ class ConnectionTest extends TestCase
*/
public function testExecuteCacheQueryStripsPlatformFromConnectionParamsBeforeGeneratingCacheKeys() : void
{
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class);
/** @var AbstractPlatform|MockObject $platform */
$platform = $this->createMock(AbstractPlatform::class);
/** @var QueryCacheProfile|MockObject $queryCacheProfile */
$queryCacheProfile = $this->createMock(QueryCacheProfile::class);
/** @var Cache|MockObject $resultCacheDriver */
$resultCacheDriver = $this->createMock(Cache::class);
$queryCacheProfile
......
......@@ -17,7 +17,6 @@ class DBALExceptionTest extends TestCase
{
public function testDriverExceptionDuringQueryAcceptsBinaryData() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), '', ['ABC', chr(128)]);
self::assertStringContainsString('with params ["ABC", "\x80"]', $e->getMessage());
......@@ -25,7 +24,6 @@ class DBALExceptionTest extends TestCase
public function testDriverExceptionDuringQueryAcceptsResource() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$e = DBALException::driverExceptionDuringQuery($driver, new Exception(), 'INSERT INTO file (`content`) VALUES (?)', [1 => fopen(__FILE__, 'r')]);
self::assertStringContainsString('Resource', $e->getMessage());
......@@ -33,10 +31,8 @@ class DBALExceptionTest extends TestCase
public function testAvoidOverWrappingOnDriverException() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
/** @var InnerDriverException $inner */
$inner = $this->createMock(InnerDriverException::class);
$ex = new DriverException('', $inner);
......
......@@ -27,7 +27,6 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use ReflectionProperty;
use function array_merge;
......@@ -79,7 +78,6 @@ abstract class AbstractDriverTest extends TestCase
self::markTestSkipped('This test is only intended for exception converter drivers.');
}
/** @var DriverExceptionInterface|MockObject $driverException */
$driverException = $this->getMockBuilder(DriverExceptionInterface::class)
->setConstructorArgs([$message])
->getMock();
......
......@@ -45,10 +45,11 @@ class MysqliConnectionTest extends FunctionalTestCase
public function testRestoresErrorHandlerOnException() : void
{
$handler = static function () : bool {
$handler = static function () : bool {
self::fail('Never expected this to be called');
};
$default_handler = set_error_handler($handler);
$defaultHandler = set_error_handler($handler);
try {
new MysqliConnection(['host' => '255.255.255.255'], 'user', 'pass');
......@@ -57,7 +58,7 @@ class MysqliConnectionTest extends FunctionalTestCase
self::assertSame('Network is unreachable', $e->getMessage());
}
self::assertSame($handler, set_error_handler($default_handler), 'Restoring error handler failed.');
self::assertSame($handler, set_error_handler($defaultHandler), 'Restoring error handler failed.');
restore_error_handler();
restore_error_handler();
}
......
......@@ -100,9 +100,6 @@ class DriverTest extends AbstractPostgreSQLDriverTest
}
}
/**
* {@inheritDoc}
*/
protected function createDriver() : DriverInterface
{
return new Driver();
......
......@@ -10,7 +10,6 @@ use Doctrine\DBAL\Driver\SQLSrv\SQLSrvStatement;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\Portability\Statement as PortabilityStatement;
use IteratorAggregate;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Traversable;
......@@ -23,7 +22,6 @@ class StatementIteratorTest extends TestCase
*/
public function testGettingIteratorDoesNotCallFetch(string $class) : void
{
/** @var IteratorAggregate|MockObject $stmt */
$stmt = $this->createPartialMock($class, ['fetch', 'fetchAll', 'fetchColumn']);
$stmt->expects(self::never())->method('fetch');
$stmt->expects(self::never())->method('fetchAll');
......@@ -71,6 +69,9 @@ class StatementIteratorTest extends TestCase
});
}
/**
* @param Traversable<int, mixed> $iterator
*/
private function assertIterationCallsFetchOncePerStep(Traversable $iterator, int &$calls) : void
{
foreach ($iterator as $i => $_) {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment