Unverified Commit 3b6e69d4 authored by Grégoire Paris's avatar Grégoire Paris

Merge branch '3.0.x'

parents ea4232a7 430dce61
......@@ -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"
......@@ -1283,16 +1283,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "0.4.2",
"version": "0.4.4",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "a6d13524641bb780efc821d9e0a1e1bfb23cbd0e"
"reference": "d8d9d4645379e677466d407034436bb155b11c65"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a6d13524641bb780efc821d9e0a1e1bfb23cbd0e",
"reference": "a6d13524641bb780efc821d9e0a1e1bfb23cbd0e",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/d8d9d4645379e677466d407034436bb155b11c65",
"reference": "d8d9d4645379e677466d407034436bb155b11c65",
"shasum": ""
},
"require": {
......@@ -1300,10 +1300,11 @@
},
"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/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan": "^0.12.19",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/phpunit": "^6.3",
"slevomat/coding-standard": "^4.7.2",
......@@ -1327,7 +1328,7 @@
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"time": "2019-12-13T12:03:22+00:00"
"time": "2020-04-13T16:28:46+00:00"
},
{
"name": "phpstan/phpstan",
......@@ -2586,33 +2587,39 @@
},
{
"name": "slevomat/coding-standard",
"version": "6.1.1",
"version": "6.3.3",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "0a7934d7ecdfe402079027513daa3b7e881f315d"
"reference": "b905a82255749de847fd4de607c7a4c8163f058d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/0a7934d7ecdfe402079027513daa3b7e881f315d",
"reference": "0a7934d7ecdfe402079027513daa3b7e881f315d",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/b905a82255749de847fd4de607c7a4c8163f058d",
"reference": "b905a82255749de847fd4de607c7a4c8163f058d",
"shasum": ""
},
"require": {
"php": "^7.1",
"phpstan/phpdoc-parser": "0.3.5 - 0.4.2",
"squizlabs/php_codesniffer": "^3.5.3"
"phpstan/phpdoc-parser": "0.4.0 - 0.4.4",
"squizlabs/php_codesniffer": "^3.5.5"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "0.5.0",
"jakub-onderka/php-parallel-lint": "1.0.0",
"phing/phing": "2.16.2",
"phpstan/phpstan": "0.11.19|0.12.5",
"phpstan/phpstan-phpunit": "0.11.2|0.12.6",
"phpstan/phpstan-strict-rules": "0.11.1|0.12.1",
"phpunit/phpunit": "7.5.18|8.5.2"
"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"
......@@ -2623,20 +2630,30 @@
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"time": "2020-01-23T15:37:30+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": {
......@@ -2674,7 +2691,7 @@
"phpcs",
"standards"
],
"time": "2019-12-04T04:46:47+00:00"
"time": "2020-04-17T01:09:41+00:00"
},
{
"name": "symfony/console",
......
......@@ -24,6 +24,8 @@
<!-- 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>
......@@ -34,6 +36,18 @@
</properties>
</rule>
<rule ref="Squiz.NamingConventions.ValidVariableName.PublicHasUnderscore">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingNativeTypeHint">
<exclude-pattern>*/src/*</exclude-pattern>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
......@@ -77,7 +91,13 @@
<!-- 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>
......
......@@ -47,6 +47,19 @@ 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\.$~'
paths:
- %currentWorkingDirectory%/src/Driver/PDOPgSql/Driver.php
- %currentWorkingDirectory%/tests/Driver/PDOPgSql/DriverTest.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\.$~'
......
......@@ -7,7 +7,7 @@ namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\Connection;
use const OCI_DEFAULT;
use const OCI_NO_AUTO_COMMIT;
/**
* A Doctrine DBAL driver for the Oracle OCI8 PHP extensions.
......@@ -29,7 +29,7 @@ final class Driver extends AbstractOracleDriver
$password,
$this->constructDsn($params),
$params['charset'] ?? '',
$params['sessionMode'] ?? OCI_DEFAULT,
$params['sessionMode'] ?? OCI_NO_AUTO_COMMIT,
$params['persistent'] ?? false
);
} catch (OCI8Exception $e) {
......
......@@ -19,7 +19,7 @@ use function oci_server_version;
use function preg_match;
use function sprintf;
use function str_replace;
use const OCI_DEFAULT;
use const OCI_NO_AUTO_COMMIT;
/**
* OCI8 implementation of the Connection interface.
......@@ -42,7 +42,7 @@ final class OCI8Connection implements Connection, ServerInfoAwareConnection
string $password,
string $db,
string $charset = '',
int $sessionMode = OCI_DEFAULT,
int $sessionMode = OCI_NO_AUTO_COMMIT,
bool $persistent = false
) {
$dbh = $persistent
......
......@@ -32,7 +32,6 @@ use const OCI_COMMIT_ON_SUCCESS;
use const OCI_D_LOB;
use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NO_AUTO_COMMIT;
use const OCI_NUM;
use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS;
......
<?php
declare(strict_types=1);
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
declare(strict_types=1);
namespace Doctrine\DBAL\Internal;
class DependencyOrderEdge
{
/** @var string */
public $from;
/** @var string */
public $to;
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Internal;
class DependencyOrderNode
{
/** @var string */
public $hash;
/** @var int */
public $state;
/** @var object */
public $value;
/** @var DependencyOrderEdge[] */
public $dependencyList = [];
}
......@@ -2807,6 +2807,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.
*/
......
......@@ -591,21 +591,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;
}
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);
}
$sql[] = $this->getCreateIndexSQL($addIndex, $table);
$sql[] = $this->getCreateIndexSQL($addIndex, $table);
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
break;
}
}
......
......@@ -547,25 +547,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;
}
$indexClause = 'INDEX ' . $addIndex->getName();
if ($addIndex->isPrimary()) {
$indexClause = 'PRIMARY KEY';
} elseif ($addIndex->isUnique()) {
$indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
}
if ($addIndex->isPrimary()) {
$indexClause = 'PRIMARY KEY';
} elseif ($addIndex->isUnique()) {
$indexClause = 'UNIQUE INDEX ' . $addIndex->getName();
}
$query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
$query .= 'ADD ' . $indexClause;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')';
$query = 'ALTER TABLE ' . $table . ' DROP INDEX ' . $remIndex->getName() . ', ';
$query .= 'ADD ' . $indexClause;
$query .= ' (' . $this->getIndexFieldDeclarationListSQL($addIndex) . ')';
$sql[] = $query;
$sql[] = $query;
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
unset($diff->removedIndexes[$remKey], $diff->addedIndexes[$addKey]);
break;
}
break;
}
}
......
......@@ -20,7 +20,7 @@ class PostgreSQL100Platform extends PostgreSQL94Platform
{
return 'SELECT sequence_name AS relname,
sequence_schema AS schemaname,
minimum_value AS min_value,
minimum_value AS min_value,
increment AS increment_by
FROM information_schema.sequences
WHERE sequence_catalog = ' . $this->quoteStringLiteral($database) . "
......
......@@ -1453,8 +1453,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
,
......
......@@ -659,6 +659,11 @@ class SqlitePlatform extends AbstractPlatform
}
public function supportsForeignKeyConstraints() : bool
{
return true;
}
public function supportsCreateDropForeignKeyConstraints() : bool
{
return false;
}
......@@ -676,7 +681,7 @@ class SqlitePlatform extends AbstractPlatform
*/
public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) : string
{
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.');
}
/**
......@@ -684,7 +689,7 @@ class SqlitePlatform extends AbstractPlatform
*/
public function getDropForeignKeySQL($foreignKey, $table) : string
{
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.');
}
/**
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
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;
}
......@@ -166,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();
}
}
......@@ -43,6 +43,10 @@ class DropSchemaSqlCollector extends AbstractVisitor
public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) : void
{
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
return;
}
if (strlen($fkConstraint->getName()) === 0) {
throw NamedForeignKeyRequired::new($localTable, $fkConstraint);
}
......
......@@ -133,13 +133,11 @@ class ConnectionTest extends TestCase
{
$eventManager = new EventManager();
/** @var AbstractPlatform|MockObject $platform */
$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')
......@@ -518,7 +516,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])
......@@ -554,7 +551,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])
......@@ -589,7 +585,6 @@ class ConnectionTest extends TestCase
->method('fetchColumn')
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -624,7 +619,6 @@ class ConnectionTest extends TestCase
->method('fetchAll')
->will(self::returnValue($result));
/** @var Connection|MockObject $conn */
$conn = $this->getMockBuilder(Connection::class)
->onlyMethods(['executeQuery'])
->setConstructorArgs([[], $driverMock])
......@@ -640,7 +634,6 @@ class ConnectionTest extends TestCase
public function testCallingDeleteWithNoDeletionCriteriaResultsInInvalidArgumentException() : void
{
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$conn = new Connection([], $driver);
......@@ -650,7 +643,6 @@ class ConnectionTest extends TestCase
public function testCallConnectOnce() : void
{
/** @var Driver|MockObject $driver */
$driver = $this->createMock(Driver::class);
$driver->expects(self::once())
->method('connect');
......@@ -667,13 +659,10 @@ class ConnectionTest extends TestCase
*/
public function testPlatformDetectionIsTriggerOnlyOnceOnRetrievingPlatform() : void
{
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class);
/** @var DriverConnection|ServerInfoAwareConnection|MockObject $driverConnectionMock */
$driverConnectionMock = $this->createMock(ServerInfoAwareConnection::class);
/** @var AbstractPlatform|MockObject $platformMock */
$platformMock = $this->getMockForAbstractClass(AbstractPlatform::class);
$connection = new Connection([], $driverMock);
......@@ -708,7 +697,6 @@ class ConnectionTest extends TestCase
$params = [666];
$types = [ParameterType::INTEGER];
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock
......@@ -723,7 +711,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(
......@@ -745,7 +732,6 @@ class ConnectionTest extends TestCase
->with('cacheKey')
->will(self::returnValue(['realKey' => []]));
/** @var QueryCacheProfile|MockObject $queryCacheProfileMock */
$queryCacheProfileMock = $this->createMock(QueryCacheProfile::class);
$queryCacheProfileMock
......@@ -765,7 +751,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);
......@@ -779,7 +764,6 @@ class ConnectionTest extends TestCase
$connectionParams = $this->params;
$connectionParams['platform'] = new stdClass();
/** @var Driver $driver */
$driver = $this->createMock(Driver::class);
$this->expectException(DBALException::class);
......@@ -792,7 +776,6 @@ class ConnectionTest extends TestCase
*/
public function testRethrowsOriginalExceptionOnDeterminingPlatformWhenConnectingToNonExistentDatabase() : void
{
/** @var VersionAwarePlatformDriver|MockObject $driverMock */
$driverMock = $this->createMock(VersionAwarePlatformDriver::class);
$connection = new Connection(['dbname' => 'foo'], $driverMock);
......@@ -817,16 +800,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
......
......@@ -21,7 +21,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());
......@@ -29,7 +28,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());
......@@ -37,10 +35,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);
......
......@@ -79,7 +79,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, $errorCode])
->getMock();
......@@ -182,7 +181,7 @@ abstract class AbstractDriverTest extends TestCase
abstract protected function createSchemaManager(Connection $connection) : AbstractSchemaManager;
/**
* @return Connection|MockObject
* @return Connection&MockObject
*/
protected function getConnectionMock() : Connection
{
......
......@@ -40,7 +40,7 @@ class StatementIteratorTest extends TestCase
}
/**
* @param Traversable<mixed> $iterator
* @param Traversable<int, mixed> $iterator
*/
private function assertIterationCallsFetchOncePerStep(Traversable $iterator, int &$calls) : void
{
......
......@@ -89,8 +89,8 @@ class MasterSlaveConnectionTest extends FunctionalTestCase
unset($params['slaves'][$index]['charset']);
}
/** @var MasterSlaveConnection $conn */
$conn = DriverManager::getConnection($params);
self::assertInstanceOf(MasterSlaveConnection::class, $conn);
$conn->connect('slave');
self::assertFalse($conn->isConnectedToMaster());
......
......@@ -16,6 +16,7 @@ use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaDiff;
use Doctrine\DBAL\Schema\Sequence;
use Doctrine\DBAL\Schema\Table;
......@@ -40,6 +41,7 @@ use function count;
use function current;
use function end;
use function explode;
use function implode;
use function in_array;
use function is_string;
use function sprintf;
......@@ -1279,4 +1281,49 @@ abstract class SchemaManagerFunctionalTestCase extends FunctionalTestCase
$table = $this->schemaManager->listTableDetails('table_with_comment');
self::assertSame('Foo with control characters \'\\', $table->getComment());
}
public function testSchemaDiffForeignKeys() : void
{
$schemaManager = $this->connection->getSchemaManager();
$platform = $this->connection->getDatabasePlatform();
$table1 = new Table('child');
$table1->addColumn('id', 'integer', ['autoincrement' => true]);
$table1->addColumn('parent_id', 'integer');
$table1->setPrimaryKey(['id']);
$table1->addForeignKeyConstraint('parent', ['parent_id'], ['id']);
$table2 = new Table('parent');
$table2->addColumn('id', 'integer', ['autoincrement' => true]);
$table2->setPrimaryKey(['id']);
$diff = new SchemaDiff(['table1' => $table1, 'table2' => $table2]);
$sqls = $diff->toSql($platform);
foreach ($sqls as $sql) {
$this->connection->exec($sql);
}
$schema = new Schema([
$schemaManager->listTableDetails('child'),
$schemaManager->listTableDetails('parent'),
]);
self::assertCount(1, $schema->getTable('child')->getForeignKeys());
$offlineSchema = new Schema([$table1, $table2]);
$diff = Comparator::compareSchemas($offlineSchema, $schema);
foreach ($diff->changedTables as $table) {
if (count($table->changedForeignKeys) <= 0 && count($table->addedForeignKeys) <= 0 && count($table->removedForeignKeys) <= 0) {
continue;
}
self::fail(
'No changes on foreigh keys should be detected, but we have: ' .
implode(', ', $diff->toSql($platform))
);
}
}
}
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Tests\Internal;
use Doctrine\DBAL\Internal\DependencyOrderCalculator;
use Doctrine\DBAL\Schema\Table;
use PHPUnit\Framework\TestCase;
/**
* Tests of the commit order calculation.
*
* IMPORTANT: When writing tests here consider that a lot of graph constellations
* can have many valid orderings, so you may want to build a graph that has only
* 1 valid order to simplify your tests.
*/
class DependencyOrderCalculatorTest extends TestCase
{
/** @var DependencyOrderCalculator */
private $calculator;
protected function setUp() : void
{
$this->calculator = new DependencyOrderCalculator();
}
public function testCommitOrdering1() : void
{
$table1 = new Table('table1');
$table2 = new Table('table2');
$table3 = new Table('table3');
$table4 = new Table('table4');
$table5 = new Table('table5');
self::assertFalse($this->calculator->hasNode($table1->getName()));
$this->calculator->addNode($table1->getName(), $table1);
$this->calculator->addNode($table2->getName(), $table2);
$this->calculator->addNode($table3->getName(), $table3);
$this->calculator->addNode($table4->getName(), $table4);
$this->calculator->addNode($table5->getName(), $table5);
self::assertTrue($this->calculator->hasNode($table1->getName()));
$this->calculator->addDependency($table1->getName(), $table2->getName());
$this->calculator->addDependency($table2->getName(), $table3->getName());
$this->calculator->addDependency($table3->getName(), $table4->getName());
$this->calculator->addDependency($table5->getName(), $table1->getName());
$sorted = $this->calculator->sort();
// There is only 1 valid ordering for this constellation
$correctOrder = [$table5, $table1, $table2, $table3, $table4];
self::assertSame($correctOrder, $sorted);
}
}
......@@ -250,6 +250,10 @@ abstract class AbstractPlatformTestCase extends TestCase
public function testGeneratesConstraintCreationSql() : void
{
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
self::markTestSkipped('Platform does not support creating or dropping foreign key constraints.');
}
$idx = new Index('constraint_name', ['test'], true, false);
$sql = $this->platform->getCreateConstraintSQL($idx, 'test');
self::assertEquals($this->getGenerateConstraintUniqueIndexSql(), $sql);
......@@ -1201,9 +1205,9 @@ abstract class AbstractPlatformTestCase extends TestCase
*/
public function testQuotesDropForeignKeySQL() : void
{
if (! $this->platform->supportsForeignKeyConstraints()) {
if (! $this->platform->supportsCreateDropForeignKeyConstraints()) {
self::markTestSkipped(
sprintf('%s does not support foreign key constraints.', get_class($this->platform))
sprintf('%s does not support modifying foreign key constraints.', get_class($this->platform))
);
}
......
......@@ -19,7 +19,7 @@ class PostgreSQL100PlatformTest extends PostgreSQL94PlatformTest
self::assertSame(
"SELECT sequence_name AS relname,
sequence_schema AS schemaname,
minimum_value AS min_value,
minimum_value AS min_value,
increment AS increment_by
FROM information_schema.sequences
WHERE sequence_catalog = 'test_db'
......
......@@ -44,12 +44,15 @@ class SchemaDiffTest extends TestCase
*/
private function createPlatform(bool $unsafe)
{
/** @var AbstractPlatform|MockObject $platform */
$platform = $this->createMock(AbstractPlatform::class);
$platform->expects(self::exactly(1))
->method('getCreateSchemaSQL')
->with('foo_ns')
->will(self::returnValue('create_schema'));
$platform->method('supportsCreateDropForeignKeyConstraints')
->will(self::returnValue(true));
if ($unsafe) {
$platform->expects(self::exactly(1))
->method('getDropSequenceSql')
......@@ -100,7 +103,7 @@ class SchemaDiffTest extends TestCase
$platform->expects(self::exactly(1))
->method('supportsSequences')
->will(self::returnValue(true));
$platform->expects(self::exactly(2))
$platform->expects(self::any())
->method('supportsForeignKeyConstraints')
->will(self::returnValue(true));
......
......@@ -2,12 +2,9 @@
declare(strict_types=1);
(static function () : void {
foreach (['ibm_db2', 'mysqli', 'oci8', 'sqlsrv', 'pgsql'] as $extension) {
if (extension_loaded($extension)) {
continue;
}
// PHPStan does not read global constants from the stubs yet, remove this when it does
if (defined('OCI_NO_AUTO_COMMIT')) {
return;
}
require sprintf(__DIR__ . '/../vendor/jetbrains/phpstorm-stubs/%1$s/%1$s.php', $extension);
}
})();
define('OCI_NO_AUTO_COMMIT', 0);
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