diff --git a/.appveyor.yml b/.appveyor.yml index 636c269d846..cfdc76aeb08 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -131,17 +131,25 @@ before_test: } test_script: - - cd C:\projects\dbal - ps: >- if ($env:db_version) { $env:phpunit_config = "tests\appveyor\$($env:db).$($env:db_version).$($env:driver).appveyor.xml" } else { $env:phpunit_config = "tests\appveyor\$($env:db).$($env:driver).appveyor.xml" } - - ps: >- + if ($env:coverage -eq "yes") { vendor\bin\phpunit -c $($env:phpunit_config) --coverage-clover clover.xml - appveyor-retry ocular code-coverage:upload --format=php-clover clover.xml } else { vendor\bin\phpunit -c $($env:phpunit_config) } + + if ($LastExitCode -ne 0) { + $host.SetShouldExit($LastExitCode) + } + +after_test: + - ps: >- + if ($env:coverage -eq "yes") { + appveyor-retry ocular code-coverage:upload --format=php-clover clover.xml + } diff --git a/.travis.yml b/.travis.yml index 6be26170ae5..40598c3374b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,9 +41,6 @@ after_script: fi jobs: - allow_failures: - - php: nightly - include: - stage: Test php: 7.1 @@ -117,7 +114,7 @@ jobs: env: DB=mysql COVERAGE=yes - stage: Test php: 7.2 - env: DB=mysql MYSQL_VERSION=5.7 COVERAGE=yes + env: DB=mysql.docker MYSQL_VERSION=5.7 COVERAGE=yes sudo: required before_script: - bash ./tests/travis/install-mysql-5.7.sh @@ -133,7 +130,7 @@ jobs: env: DB=mysqli COVERAGE=yes - stage: Test php: 7.2 - env: DB=mysqli MYSQL_VERSION=5.7 COVERAGE=yes + env: DB=mysqli.docker MYSQL_VERSION=5.7 COVERAGE=yes sudo: required before_script: - bash ./tests/travis/install-mysql-5.7.sh @@ -319,31 +316,31 @@ jobs: - bash ./tests/travis/install-mssql-pdo_sqlsrv.sh - bash ./tests/travis/install-mssql.sh - stage: Test - php: nightly + php: 7.4snapshot env: DB=mysql MYSQL_VERSION=8.0 dist: xenial sudo: required before_script: - bash ./tests/travis/install-mysql-8.0.sh - stage: Test - php: nightly + php: 7.4snapshot env: DB=mysqli MYSQL_VERSION=8.0 dist: xenial sudo: required before_script: - bash ./tests/travis/install-mysql-8.0.sh - stage: Test - php: nightly + php: 7.4snapshot env: DB=mariadb MARIADB_VERSION=10.3 addons: mariadb: 10.3 - stage: Test - php: nightly + php: 7.4snapshot env: DB=mariadb.mysqli MARIADB_VERSION=10.3 addons: mariadb: 10.3 - stage: Test - php: nightly + php: 7.4snapshot env: DB=pgsql POSTGRESQL_VERSION=11.0 sudo: required services: @@ -351,10 +348,10 @@ jobs: before_script: - bash ./tests/travis/install-postgres-11.sh - stage: Test - php: nightly + php: 7.4snapshot env: DB=sqlite - stage: Test - php: nightly + php: 7.4snapshot env: DB=sqlsrv sudo: required services: @@ -363,7 +360,7 @@ jobs: - bash ./tests/travis/install-mssql-sqlsrv.sh - bash ./tests/travis/install-mssql.sh - stage: Test - php: nightly + php: 7.4snapshot env: DB=pdo_sqlsrv sudo: required services: diff --git a/docs/en/index.rst b/docs/en/index.rst index 1e8fee0c190..1bdabab85f3 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -10,7 +10,7 @@ Getting Help If this documentation is not helping to answer questions you have about the Doctrine DBAL, don't panic. You can get help from different sources: -- Gitter chat room `#doctrine/dbal `_ +- Slack chat room `#dbal `_ - On `Stack Overflow `_ - The `Doctrine Mailing List `_ - Report a bug on `GitHub `_. diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 872f3e71fc9..35e7c986bc6 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -166,7 +166,18 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { - return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + $data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + + if ($fetchMode === FetchMode::COLUMN) { + foreach ($data as $key => $value) { + $data[$key] = [$value]; + } + } + + $this->data = $data; + $this->emptied = true; + + return $this->data; } /** diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index 85761fcca9b..3f4fe5a59a1 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -356,6 +356,8 @@ public function connect() $this->_conn = $this->_driver->connect($this->params, $user, $password, $driverOptions); $this->isConnected = true; + $this->transactionNestingLevel = 0; + if ($this->autoCommit === false) { $this->beginTransaction(); } diff --git a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php index e9864d6df90..d2e4b2c2509 100644 --- a/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php +++ b/lib/Doctrine/DBAL/Connections/MasterSlaveConnection.php @@ -354,6 +354,8 @@ public function query() $statement = $this->_conn->query(...$args); + $statement->setFetchMode($this->defaultFetchMode); + if ($logger) { $logger->stopQuery(); } diff --git a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php index 9d61ad42d08..af3a9d1a7a9 100644 --- a/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php +++ b/lib/Doctrine/DBAL/Driver/OCI8/OCI8Exception.php @@ -7,12 +7,16 @@ class OCI8Exception extends AbstractDriverException { /** - * @param mixed[] $error + * @param mixed[]|false $error * * @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception */ public static function fromErrorInfo($error) { + if ($error === false) { + return new self('Database error occurred but no error information was retrieved from the driver.'); + } + return new self($error['message'], null, $error['code']); } } diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index 0ed2d454624..6354836a478 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -59,6 +59,9 @@ class SQLAnywhereStatement implements IteratorAggregate, Statement /** @var resource The prepared SQL statement to execute. */ private $stmt; + /** @var mixed[] The references to bound parameter values. */ + private $boundValues = []; + /** * Prepares given statement for given connection. * @@ -108,6 +111,8 @@ public function bindParam($column, &$variable, $type = ParameterType::STRING, $l throw new SQLAnywhereException('Unknown type: ' . $type); } + $this->boundValues[$column] =& $variable; + if (! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) { throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt); } @@ -248,19 +253,19 @@ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = n switch ($fetchMode) { case FetchMode::CUSTOM_OBJECT: - while ($row = $this->fetch(...func_get_args())) { + while (($row = $this->fetch(...func_get_args())) !== false) { $rows[] = $row; } break; case FetchMode::COLUMN: - while ($row = $this->fetchColumn()) { + while (($row = $this->fetchColumn()) !== false) { $rows[] = $row; } break; default: - while ($row = $this->fetch($fetchMode)) { + while (($row = $this->fetch($fetchMode)) !== false) { $rows[] = $row; } } diff --git a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php index a8fbfeb46e4..ec3ef933441 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php @@ -1308,25 +1308,20 @@ protected function _getTransactionIsolationLevelSQL($level) */ protected function doModifyLimitQuery($query, $limit, $offset) { - $limitOffsetClause = ''; + $limitOffsetClause = $this->getTopClauseSQL($limit, $offset); - if ($limit > 0) { - $limitOffsetClause = 'TOP ' . $limit . ' '; - } + return $limitOffsetClause === '' + ? $query + : preg_replace('/^\s*(SELECT\s+(DISTINCT\s+)?)/i', '\1' . $limitOffsetClause . ' ', $query); + } + private function getTopClauseSQL(?int $limit, ?int $offset) : string + { if ($offset > 0) { - if ($limit === 0) { - $limitOffsetClause = 'TOP ALL '; - } - - $limitOffsetClause .= 'START AT ' . ($offset + 1) . ' '; - } - - if ($limitOffsetClause) { - return preg_replace('/^\s*(SELECT\s+(DISTINCT\s+)?)/i', '\1' . $limitOffsetClause, $query); + return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1); } - return $query; + return $limit === null ? '' : 'TOP ' . $limit; } /** diff --git a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php index dc8775e61ab..670b4596645 100644 --- a/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SQLServerPlatform.php @@ -10,7 +10,6 @@ use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\DBAL\Types; use InvalidArgumentException; use function array_merge; use function array_unique; @@ -1591,36 +1590,6 @@ public function getBlobTypeDeclarationSQL(array $field) return 'VARBINARY(MAX)'; } - /** - * {@inheritDoc} - */ - public function getDefaultValueDeclarationSQL($field) - { - if (! isset($field['default'])) { - return empty($field['notnull']) ? ' NULL' : ''; - } - - if (! isset($field['type'])) { - return " DEFAULT '" . $field['default'] . "'"; - } - - $type = $field['type']; - - if ($type instanceof Types\PhpIntegerMappingType) { - return ' DEFAULT ' . $field['default']; - } - - if ($type instanceof Types\PhpDateTimeMappingType && $field['default'] === $this->getCurrentTimestampSQL()) { - return ' DEFAULT ' . $this->getCurrentTimestampSQL(); - } - - if ($type instanceof Types\BooleanType) { - return " DEFAULT '" . $this->convertBooleans($field['default']) . "'"; - } - - return " DEFAULT '" . $field['default'] . "'"; - } - /** * {@inheritdoc} * diff --git a/lib/Doctrine/DBAL/Schema/Comparator.php b/lib/Doctrine/DBAL/Schema/Comparator.php index 3b5a0b155d0..2de65b11aa8 100644 --- a/lib/Doctrine/DBAL/Schema/Comparator.php +++ b/lib/Doctrine/DBAL/Schema/Comparator.php @@ -11,6 +11,7 @@ use function array_shift; use function array_unique; use function count; +use function get_class; use function strtolower; /** @@ -417,7 +418,11 @@ public function diffColumn(Column $column1, Column $column2) $changedProperties = []; - foreach (['type', 'notnull', 'unsigned', 'autoincrement'] as $property) { + if (get_class($properties1['type']) !== get_class($properties2['type'])) { + $changedProperties[] = 'type'; + } + + foreach (['notnull', 'unsigned', 'autoincrement'] as $property) { if ($properties1[$property] === $properties2[$property]) { continue; } @@ -435,7 +440,7 @@ public function diffColumn(Column $column1, Column $column2) // Null values need to be checked additionally as they tell whether to create or drop a default value. // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. if (($properties1['default'] === null) !== ($properties2['default'] === null) - || (string) $properties1['default'] !== (string) $properties2['default']) { + || $properties1['default'] != $properties2['default']) { $changedProperties[] = 'default'; } diff --git a/lib/Doctrine/DBAL/Schema/Index.php b/lib/Doctrine/DBAL/Schema/Index.php index bae6d218cb6..91ffd472465 100644 --- a/lib/Doctrine/DBAL/Schema/Index.php +++ b/lib/Doctrine/DBAL/Schema/Index.php @@ -4,6 +4,7 @@ use Doctrine\DBAL\Platforms\AbstractPlatform; use InvalidArgumentException; +use function array_filter; use function array_keys; use function array_map; use function array_search; @@ -211,6 +212,10 @@ public function isFullfilledBy(Index $other) return false; } + if (! $this->hasSameColumnLengths($other)) { + return false; + } + if (! $this->isUnique() && ! $this->isPrimary()) { // this is a special case: If the current key is neither primary or unique, any unique or // primary key will always have the same effect for the index and there cannot be any constraint @@ -336,4 +341,17 @@ private function samePartialIndex(Index $other) return ! $this->hasOption('where') && ! $other->hasOption('where'); } + + /** + * Returns whether the index has the same column lengths as the other + */ + private function hasSameColumnLengths(self $other) : bool + { + $filter = static function (?int $length) : bool { + return $length !== null; + }; + + return array_filter($this->options['lengths'] ?? [], $filter) + === array_filter($other->options['lengths'] ?? [], $filter); + } } diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index b6332026cdd..c27e8308845 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -18,7 +18,6 @@ use function strpos; use function strtok; use function strtolower; -use function trim; /** * Schema manager for the MySql RDBMS. @@ -69,7 +68,7 @@ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { $v['flags'] = ['SPATIAL']; } - $v['length'] = $v['sub_part'] ?? null; + $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; $tableIndexes[$k] = $v; } @@ -297,33 +296,43 @@ public function listTableDetails($tableName) $tableOptions = $this->_conn->fetchAssoc($sql); + if ($tableOptions === false) { + return $table; + } + $table->addOption('engine', $tableOptions['ENGINE']); + if ($tableOptions['TABLE_COLLATION'] !== null) { $table->addOption('collation', $tableOptions['TABLE_COLLATION']); } + if ($tableOptions['AUTO_INCREMENT'] !== null) { $table->addOption('autoincrement', $tableOptions['AUTO_INCREMENT']); } + $table->addOption('comment', $tableOptions['TABLE_COMMENT']); + $table->addOption('create_options', $this->parseCreateOptions($tableOptions['CREATE_OPTIONS'])); - if ($tableOptions['CREATE_OPTIONS'] === null) { - return $table; - } + return $table; + } - $createOptionsString = trim($tableOptions['CREATE_OPTIONS']); + /** + * @return string[]|true[] + */ + private function parseCreateOptions(?string $string) : array + { + $options = []; - $createOptions = []; + if ($string === null || $string === '') { + return $options; + } - if ($createOptionsString !== '') { - foreach (explode(' ', $createOptionsString) as $option) { - [$createOption, $value] = explode('=', $option); + foreach (explode(' ', $string) as $pair) { + $parts = explode('=', $pair, 2); - $createOptions[$createOption] = $value; - } + $options[$parts[0]] = $parts[1] ?? true; } - $table->addOption('create_options', $createOptions); - - return $table; + return $options; } } diff --git a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php index c4d05aa1858..b9919f58f14 100644 --- a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php @@ -128,7 +128,7 @@ protected function _getPortableTableColumnDefinition($tableColumn) } } - $unsigned = $fixed = null; + $unsigned = $fixed = $precision = $scale = $length = null; if (! isset($tableColumn['column_name'])) { $tableColumn['column_name'] = ''; @@ -146,8 +146,13 @@ protected function _getPortableTableColumnDefinition($tableColumn) $tableColumn['data_default'] = trim($tableColumn['data_default'], "'"); } - $precision = null; - $scale = null; + if ($tableColumn['data_precision'] !== null) { + $precision = (int) $tableColumn['data_precision']; + } + + if ($tableColumn['data_scale'] !== null) { + $scale = (int) $tableColumn['data_scale']; + } $type = $this->_platform->getDoctrineTypeMapping($dbType); $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); @@ -155,28 +160,16 @@ protected function _getPortableTableColumnDefinition($tableColumn) switch ($dbType) { case 'number': - if ($tableColumn['data_precision'] === 20 && $tableColumn['data_scale'] === 0) { - $precision = 20; - $scale = 0; - $type = 'bigint'; - } elseif ($tableColumn['data_precision'] === 5 && $tableColumn['data_scale'] === 0) { - $type = 'smallint'; - $precision = 5; - $scale = 0; - } elseif ($tableColumn['data_precision'] === 1 && $tableColumn['data_scale'] === 0) { - $precision = 1; - $scale = 0; - $type = 'boolean'; - } elseif ($tableColumn['data_scale'] > 0) { - $precision = $tableColumn['data_precision']; - $scale = $tableColumn['data_scale']; - $type = 'decimal'; + if ($precision === 20 && $scale === 0) { + $type = 'bigint'; + } elseif ($precision === 5 && $scale === 0) { + $type = 'smallint'; + } elseif ($precision === 1 && $scale === 0) { + $type = 'boolean'; + } elseif ($scale > 0) { + $type = 'decimal'; } - $length = null; - break; - case 'pls_integer': - case 'binary_integer': - $length = null; + break; case 'varchar': case 'varchar2': @@ -189,31 +182,6 @@ protected function _getPortableTableColumnDefinition($tableColumn) $length = $tableColumn['char_length']; $fixed = true; break; - case 'date': - case 'timestamp': - $length = null; - break; - case 'float': - case 'binary_float': - case 'binary_double': - $precision = $tableColumn['data_precision']; - $scale = $tableColumn['data_scale']; - $length = null; - break; - case 'clob': - case 'nclob': - $length = null; - break; - case 'blob': - case 'raw': - case 'long raw': - case 'bfile': - $length = null; - break; - case 'rowid': - case 'urowid': - default: - $length = null; } $options = [ diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 5dac5bb1a34..c89298bb015 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -9,8 +9,6 @@ use function array_filter; use function array_merge; use function in_array; -use function is_numeric; -use function is_string; use function preg_match; use function strlen; use function strtolower; @@ -39,7 +37,9 @@ class Table extends AbstractAsset protected $_fkConstraints = []; /** @var mixed[] */ - protected $_options = []; + protected $_options = [ + 'create_options' => [], + ]; /** @var SchemaConfig|null */ protected $_schemaConfig = null; @@ -74,7 +74,7 @@ public function __construct($tableName, array $columns = [], array $indexes = [] $this->_addForeignKeyConstraint($constraint); } - $this->_options = $options; + $this->_options = array_merge($this->_options, $options); } /** @@ -100,16 +100,16 @@ protected function _getMaxIdentifierLength() /** * Sets the Primary Key. * - * @param mixed[][] $columns + * @param string[] $columnNames * @param string|bool $indexName * * @return self */ - public function setPrimaryKey(array $columns, $indexName = false) + public function setPrimaryKey(array $columnNames, $indexName = false) { - $this->_addIndex($this->_createIndex($columns, $indexName ?: 'primary', true, true)); + $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true)); - foreach ($columns as $columnName) { + foreach ($columnNames as $columnName) { $column = $this->getColumn($columnName); $column->setNotnull(true); } @@ -118,7 +118,7 @@ public function setPrimaryKey(array $columns, $indexName = false) } /** - * @param mixed[][] $columnNames + * @param string[] $columnNames * @param string|null $indexName * @param string[] $flags * @param mixed[] $options @@ -168,7 +168,7 @@ public function dropIndex($indexName) } /** - * @param mixed[][] $columnNames + * @param string[] $columnNames * @param string|null $indexName * @param mixed[] $options * @@ -236,15 +236,15 @@ public function renameIndex($oldIndexName, $newIndexName = null) /** * Checks if an index begins in the order of the given columns. * - * @param mixed[][] $columnsNames + * @param string[] $columnNames * * @return bool */ - public function columnsAreIndexed(array $columnsNames) + public function columnsAreIndexed(array $columnNames) { foreach ($this->getIndexes() as $index) { /** @var $index Index */ - if ($index->spansColumns($columnsNames)) { + if ($index->spansColumns($columnNames)) { return true; } } @@ -253,12 +253,12 @@ public function columnsAreIndexed(array $columnsNames) } /** - * @param mixed[][] $columnNames - * @param string $indexName - * @param bool $isUnique - * @param bool $isPrimary - * @param string[] $flags - * @param mixed[] $options + * @param string[] $columnNames + * @param string $indexName + * @param bool $isUnique + * @param bool $isPrimary + * @param string[] $flags + * @param mixed[] $options * * @return Index * @@ -270,11 +270,7 @@ private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrim throw SchemaException::indexNameInvalid($indexName); } - foreach ($columnNames as $columnName => $indexColOptions) { - if (is_numeric($columnName) && is_string($indexColOptions)) { - $columnName = $indexColOptions; - } - + foreach ($columnNames as $columnName) { if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index 570f56afba0..941d621da46 100644 --- a/lib/Doctrine/DBAL/Version.php +++ b/lib/Doctrine/DBAL/Version.php @@ -14,7 +14,7 @@ class Version /** * Current Doctrine Version. */ - public const VERSION = '2.9.0'; + public const VERSION = '2.9.3'; /** * Compares a Doctrine version with the current one. diff --git a/phpcs.xml.dist b/phpcs.xml.dist index aaa6b5f04a5..c38da21aa70 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -58,4 +58,9 @@ tests/Doctrine/Tests/DBAL/Platforms/OraclePlatformTest.php tests/Doctrine/Tests/DBAL/SQLParserUtilsTest.php + + + + lib/Doctrine/DBAL/Schema/Comparator.php + diff --git a/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php b/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php index b51f3bf2f5b..d6e73f57412 100644 --- a/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php +++ b/tests/Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php @@ -60,6 +60,11 @@ public function testExecute(array $params) $this->equalTo($params[2]) ); + // the return value is irrelevant to the test + // but it has to be compatible with the method signature + $statement->method('errorInfo') + ->willReturn(false); + // can't pass to constructor since we don't have a real database handle, // but execute must check the connection for the executeMode $conn = $this->getMockBuilder(OCI8Connection::class) diff --git a/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php index b9d03d5e66c..6da314781b7 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ConnectionTest.php @@ -69,6 +69,40 @@ public function testTransactionNestingBehavior() $this->connection->rollBack(); self::assertEquals(0, $this->connection->getTransactionNestingLevel()); } + + $this->connection->beginTransaction(); + $this->connection->close(); + $this->connection->beginTransaction(); + self::assertEquals(1, $this->connection->getTransactionNestingLevel()); + } + + public function testTransactionNestingLevelIsResetOnReconnect() : void + { + if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') { + $params = $this->connection->getParams(); + $params['memory'] = false; + $params['path'] = '/tmp/test_nesting.sqlite'; + + $connection = DriverManager::getConnection( + $params, + $this->connection->getConfiguration(), + $this->connection->getEventManager() + ); + } else { + $connection = $this->connection; + } + + $connection->executeQuery('CREATE TABLE test_nesting(test int not null)'); + + $this->connection->beginTransaction(); + $this->connection->beginTransaction(); + $connection->close(); // connection closed in runtime (for example if lost or another application logic) + + $connection->beginTransaction(); + $connection->executeQuery('insert into test_nesting values (33)'); + $connection->rollback(); + + self::assertEquals(0, $connection->fetchColumn('select count(*) from test_nesting')); } public function testTransactionNestingBehaviorWithSavepoints() diff --git a/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php index 8c274e1cbb2..bbd8d5ebd09 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php @@ -61,6 +61,7 @@ private function getConnection(array $driverOptions) [ 'host' => $GLOBALS['db_host'], 'dbname' => $GLOBALS['db_name'], + 'port' => $GLOBALS['db_port'], ], $GLOBALS['db_username'], $GLOBALS['db_password'], diff --git a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php index e420000ba58..5ca5e95894f 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ExceptionTest.php @@ -3,13 +3,19 @@ namespace Doctrine\Tests\DBAL\Functional; use Doctrine\DBAL\Driver\ExceptionConverterDriver; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Platforms\DrizzlePlatform; +use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; use Doctrine\Tests\DbalFunctionalTestCase; use Throwable; use function array_merge; +use function assert; use function chmod; use function defined; use function file_exists; @@ -17,6 +23,7 @@ use function sys_get_temp_dir; use function touch; use function unlink; +use function version_compare; class ExceptionTest extends DbalFunctionalTestCase { @@ -289,7 +296,7 @@ public function testSyntaxErrorException() */ public function testConnectionExceptionSqLite($mode, $exceptionClass) { - if ($this->connection->getDatabasePlatform()->getName() !== 'sqlite') { + if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) { $this->markTestSkipped('Only fails this way on sqlite'); } @@ -333,18 +340,29 @@ public function getSqLiteOpenConnection() */ public function testConnectionException($params) { - if ($this->connection->getDatabasePlatform()->getName() === 'sqlite') { + $platform = $this->connection->getDatabasePlatform(); + + if ($platform instanceof SqlitePlatform) { $this->markTestSkipped('Only skipped if platform is not sqlite'); } - if ($this->connection->getDatabasePlatform()->getName() === 'drizzle') { + if ($platform instanceof DrizzlePlatform) { $this->markTestSkipped('Drizzle does not always support authentication'); } - if ($this->connection->getDatabasePlatform()->getName() === 'postgresql' && isset($params['password'])) { + if ($platform instanceof PostgreSqlPlatform && isset($params['password'])) { $this->markTestSkipped('Does not work on Travis'); } + if ($platform instanceof MySqlPlatform && isset($params['user'])) { + $wrappedConnection = $this->connection->getWrappedConnection(); + assert($wrappedConnection instanceof ServerInfoAwareConnection); + + if (version_compare($wrappedConnection->getServerVersion(), '8', '>=')) { + $this->markTestIncomplete('PHP currently does not completely support MySQL 8'); + } + } + $defaultParams = $this->connection->getParams(); $params = array_merge($defaultParams, $params); diff --git a/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php b/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php index 4815006335d..00dc7e876eb 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/MasterSlaveConnectionTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\DBAL\Functional; use Doctrine\DBAL\Connections\MasterSlaveConnection; +use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Schema\Table; use Doctrine\Tests\DbalFunctionalTestCase; @@ -187,4 +188,54 @@ public function testMasterSlaveConnectionCloseAndReconnect() $conn->connect('master'); self::assertTrue($conn->isConnectedToMaster()); } + + public function testQueryOnMaster() + { + $conn = $this->createMasterSlaveConnection(); + + $query = 'SELECT count(*) as num FROM master_slave_table'; + + $statement = $conn->query($query); + + self::assertInstanceOf(Statement::class, $statement); + + //Query must be executed only on Master + self::assertTrue($conn->isConnectedToMaster()); + + $data = $statement->fetchAll(); + + //Default fetchmode is FetchMode::ASSOCIATIVE + self::assertArrayHasKey(0, $data); + self::assertArrayHasKey('num', $data[0]); + + //Could be set in other fetchmodes + self::assertArrayNotHasKey(0, $data[0]); + self::assertEquals(1, $data[0]['num']); + } + + public function testQueryOnSlave() + { + $conn = $this->createMasterSlaveConnection(); + $conn->connect('slave'); + + $query = 'SELECT count(*) as num FROM master_slave_table'; + + $statement = $conn->query($query); + + self::assertInstanceOf(Statement::class, $statement); + + //Query must be executed only on Master, even when we connect to the slave + self::assertTrue($conn->isConnectedToMaster()); + + $data = $statement->fetchAll(); + + //Default fetchmode is FetchMode::ASSOCIATIVE + self::assertArrayHasKey(0, $data); + self::assertArrayHasKey('num', $data[0]); + + //Could be set in other fetchmodes + self::assertArrayNotHasKey(0, $data[0]); + + self::assertEquals(1, $data[0]['num']); + } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Platform/DefaultExpressionTest.php b/tests/Doctrine/Tests/DBAL/Functional/Platform/DefaultExpressionTest.php new file mode 100644 index 00000000000..bddc884c105 --- /dev/null +++ b/tests/Doctrine/Tests/DBAL/Functional/Platform/DefaultExpressionTest.php @@ -0,0 +1,78 @@ +connection->getDatabasePlatform(); + + if ($platform instanceof MySqlPlatform) { + self::markTestSkipped('Not supported on MySQL'); + } + + $this->assertDefaultExpression(Type::DATE, static function (AbstractPlatform $platform) : string { + return $platform->getCurrentDateSQL(); + }); + } + + public function testCurrentTime() : void + { + $platform = $this->connection->getDatabasePlatform(); + + if ($platform instanceof MySqlPlatform) { + self::markTestSkipped('Not supported on MySQL'); + } + + if ($platform instanceof OraclePlatform) { + self::markTestSkipped('Not supported on Oracle'); + } + + $this->assertDefaultExpression(Type::TIME, static function (AbstractPlatform $platform) : string { + return $platform->getCurrentTimeSQL(); + }); + } + + public function testCurrentTimestamp() : void + { + $this->assertDefaultExpression(Type::DATETIME, static function (AbstractPlatform $platform) : string { + return $platform->getCurrentTimestampSQL(); + }); + } + + private function assertDefaultExpression(string $type, callable $expression) : void + { + $platform = $this->connection->getDatabasePlatform(); + $defaultSql = $expression($platform, $this); + + $table = new Table('default_expr_test'); + $table->addColumn('actual_value', $type); + $table->addColumn('default_value', $type, ['default' => $defaultSql]); + $this->connection->getSchemaManager()->dropAndCreateTable($table); + + $this->connection->exec( + sprintf( + 'INSERT INTO default_expr_test (actual_value) VALUES (%s)', + $defaultSql + ) + ); + + [$actualValue, $defaultValue] = $this->connection->query( + 'SELECT default_value, actual_value FROM default_expr_test' + )->fetch(FetchMode::NUMERIC); + + self::assertEquals($actualValue, $defaultValue); + } +} diff --git a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php index ae4259da91f..caf90927c3b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php @@ -166,6 +166,32 @@ public function testDontFinishNoCache() self::assertCount(2, $this->sqlLogger->queries); } + public function testFetchAllAndFinishSavesCache() + { + $layerCache = new ArrayCache(); + $stmt = $this->connection->executeQuery('SELECT * FROM caching WHERE test_int > 500', [], [], new QueryCacheProfile(10, 'testcachekey', $layerCache)); + $stmt->fetchAll(); + $stmt->closeCursor(); + + self::assertCount(1, $layerCache->fetch('testcachekey')); + } + + public function testFetchAllColumn() : void + { + $query = $this->connection->getDatabasePlatform() + ->getDummySelectSQL('1'); + + $qcp = new QueryCacheProfile(0, 0, new ArrayCache()); + + $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp); + $stmt->fetchAll(FetchMode::COLUMN); + $stmt->closeCursor(); + + $stmt = $this->connection->executeCacheQuery($query, [], [], $qcp); + + self::assertEquals([1], $stmt->fetchAll(FetchMode::COLUMN)); + } + public function assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, $fetchMode) { $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/ComparatorTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/ComparatorTest.php index a97eb90bbce..0feb30fa5ba 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/ComparatorTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/ComparatorTest.php @@ -25,15 +25,31 @@ protected function setUp() $this->comparator = new Comparator(); } - public function testDefaultValueComparison() + /** + * @param mixed $value + * + * @dataProvider defaultValueProvider + */ + public function testDefaultValueComparison(string $type, $value) : void { $table = new Table('default_value'); - $table->addColumn('id', 'integer', ['default' => 1]); + $table->addColumn('test', $type, ['default' => $value]); - $this->schemaManager->createTable($table); + $this->schemaManager->dropAndCreateTable($table); $onlineTable = $this->schemaManager->listTableDetails('default_value'); self::assertFalse($this->comparator->diffTable($table, $onlineTable)); } + + /** + * @return mixed[][] + */ + public static function defaultValueProvider() : iterable + { + return [ + ['integer', 1], + ['boolean', false], + ]; + } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 475dcee3ee6..5bf0ad1712f 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -107,6 +107,19 @@ public function testSpatialIndex() self::assertTrue($indexes['s_index']->hasFlag('spatial')); } + public function testIndexWithLength() : void + { + $table = new Table('index_length'); + $table->addColumn('text', 'string', ['length' => 255]); + $table->addIndex(['text'], 'text_index', [], ['lengths' => [128]]); + + $this->schemaManager->dropAndCreateTable($table); + + $indexes = $this->schemaManager->listTableIndexes('index_length'); + self::assertArrayHasKey('text_index', $indexes); + self::assertSame([128], $indexes['text_index']->getOption('lengths')); + } + /** * @group DBAL-400 */ @@ -502,6 +515,7 @@ public function testEnsureTableOptionsAreReflectedInMetadata() : void ROW_FORMAT COMPRESSED COMMENT 'This is a test' AUTO_INCREMENT=42 +PARTITION BY HASH (col1) SQL; $this->connection->query($sql); @@ -511,7 +525,10 @@ public function testEnsureTableOptionsAreReflectedInMetadata() : void self::assertEquals('utf8_general_ci', $onlineTable->getOption('collation')); self::assertEquals(42, $onlineTable->getOption('autoincrement')); self::assertEquals('This is a test', $onlineTable->getOption('comment')); - self::assertEquals(['row_format' => 'COMPRESSED'], $onlineTable->getOption('create_options')); + self::assertEquals([ + 'row_format' => 'COMPRESSED', + 'partitioned' => true, + ], $onlineTable->getOption('create_options')); } public function testEnsureTableWithoutOptionsAreReflectedInMetadata() : void @@ -527,4 +544,11 @@ public function testEnsureTableWithoutOptionsAreReflectedInMetadata() : void self::assertEquals('', $onlineTable->getOption('comment')); self::assertEquals([], $onlineTable->getOption('create_options')); } + + public function testParseNullCreateOptions() : void + { + $table = $this->schemaManager->listTableDetails('sys.processlist'); + + self::assertEquals([], $table->getOption('create_options')); + } } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php index f48c5157256..f076a806a7a 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php @@ -24,7 +24,7 @@ public function testCreateAndListViews() self::assertCount(1, $views, 'Database has to have one view.'); self::assertInstanceOf(View::class, $views[$name]); self::assertEquals($name, $views[$name]->getName()); - self::assertEquals($sql, $views[$name]->getSql()); + self::assertRegExp('/^SELECT \* from "?DBA"?\."?view_test_table"?$/', $views[$name]->getSql()); } public function testDropAndCreateAdvancedIndex() diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLServerSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLServerSchemaManagerTest.php index cb7f37f15d5..d4598bccdb8 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLServerSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/SQLServerSchemaManagerTest.php @@ -54,8 +54,7 @@ public function testColumnCollation() public function testDefaultConstraints() { - $platform = $this->schemaManager->getDatabasePlatform(); - $table = new Table('sqlsrv_default_constraints'); + $table = new Table('sqlsrv_default_constraints'); $table->addColumn('no_default', 'string'); $table->addColumn('df_integer', 'integer', ['default' => 666]); $table->addColumn('df_string_1', 'string', ['default' => 'foobar']); @@ -63,8 +62,6 @@ public function testDefaultConstraints() $table->addColumn('df_string_3', 'string', ['default' => 'another default value']); $table->addColumn('df_string_4', 'string', ['default' => 'column to rename']); $table->addColumn('df_boolean', 'boolean', ['default' => true]); - $table->addColumn('df_current_date', 'date', ['default' => $platform->getCurrentDateSQL()]); - $table->addColumn('df_current_time', 'time', ['default' => $platform->getCurrentTimeSQL()]); $this->schemaManager->createTable($table); $columns = $this->schemaManager->listTableColumns('sqlsrv_default_constraints'); @@ -75,12 +72,10 @@ public function testDefaultConstraints() self::assertEquals('Doctrine rocks!!!', $columns['df_string_2']->getDefault()); self::assertEquals('another default value', $columns['df_string_3']->getDefault()); self::assertEquals(1, $columns['df_boolean']->getDefault()); - self::assertSame($platform->getCurrentDateSQL(), $columns['df_current_date']->getDefault()); - self::assertSame($platform->getCurrentTimeSQL(), $columns['df_current_time']->getDefault()); $diff = new TableDiff( 'sqlsrv_default_constraints', - [new Column('df_current_timestamp', Type::getType('datetime'), ['default' => 'CURRENT_TIMESTAMP'])], + [], [ 'df_integer' => new ColumnDiff( 'df_integer', @@ -126,7 +121,6 @@ public function testDefaultConstraints() $columns = $this->schemaManager->listTableColumns('sqlsrv_default_constraints'); self::assertNull($columns['no_default']->getDefault()); - self::assertEquals('CURRENT_TIMESTAMP', $columns['df_current_timestamp']->getDefault()); self::assertEquals(0, $columns['df_integer']->getDefault()); self::assertNull($columns['df_string_2']->getDefault()); self::assertEquals('another default value', $columns['df_string_3']->getDefault()); @@ -140,12 +134,6 @@ public function testDefaultConstraints() 'sqlsrv_default_constraints', [], [ - 'df_current_timestamp' => new ColumnDiff( - 'df_current_timestamp', - new Column('df_current_timestamp', Type::getType('datetime')), - ['default'], - new Column('df_current_timestamp', Type::getType('datetime'), ['default' => 'CURRENT_TIMESTAMP']) - ), 'df_integer' => new ColumnDiff( 'df_integer', new Column('df_integer', Type::getType('integer'), ['default' => 666]), @@ -163,7 +151,6 @@ public function testDefaultConstraints() $this->schemaManager->alterTable($diff); $columns = $this->schemaManager->listTableColumns('sqlsrv_default_constraints'); - self::assertNull($columns['df_current_timestamp']->getDefault()); self::assertEquals(666, $columns['df_integer']->getDefault()); } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php index 1341c9e8819..00c941d6d09 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/AbstractSQLServerPlatformTestCase.php @@ -1494,7 +1494,7 @@ public function testGetDefaultValueDeclarationSQLForDateType() : void ]; self::assertSame( - " DEFAULT '" . $currentDateSql . "'", + ' DEFAULT CONVERT(date, GETDATE())', $this->platform->getDefaultValueDeclarationSQL($field) ); } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php index 294b9836e2e..219119c2cc1 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/SQLAnywherePlatformTest.php @@ -646,7 +646,7 @@ public function testModifiesLimitQueryWithOffset() $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 5) ); self::assertEquals( - 'SELECT TOP ALL START AT 6 * FROM user', + 'SELECT TOP 0 START AT 6 * FROM user', $this->platform->modifyLimitQuery('SELECT * FROM user', 0, 5) ); } @@ -659,6 +659,14 @@ public function testModifiesLimitQueryWithSubSelect() ); } + public function testModifiesLimitQueryWithoutLimit() + { + self::assertEquals( + 'SELECT TOP ALL START AT 11 n FROM Foo', + $this->platform->modifyLimitQuery('SELECT n FROM Foo', null, 10) + ); + } + public function testPrefersIdentityColumns() { self::assertTrue($this->platform->prefersIdentityColumns()); diff --git a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php index 4376448072d..6113653d20c 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/ComparatorTest.php @@ -16,6 +16,7 @@ use Doctrine\DBAL\Types\Type; use PHPUnit\Framework\TestCase; use function array_keys; +use function get_class; class ComparatorTest extends TestCase { @@ -193,6 +194,36 @@ public function testCompareChangedColumnsChangeType() self::assertEquals([], $c->diffColumn($column1, $column1)); } + public function testCompareColumnsMultipleTypeInstances() : void + { + $integerType1 = Type::getType('integer'); + Type::overrideType('integer', get_class($integerType1)); + $integerType2 = Type::getType('integer'); + + $column1 = new Column('integerfield1', $integerType1); + $column2 = new Column('integerfield1', $integerType2); + + $c = new Comparator(); + self::assertEquals([], $c->diffColumn($column1, $column2)); + } + + public function testCompareColumnsOverriddenType() : void + { + $oldStringInstance = Type::getType('string'); + $integerType = Type::getType('integer'); + + Type::overrideType('string', get_class($integerType)); + $overriddenStringType = Type::getType('string'); + + Type::overrideType('string', get_class($oldStringInstance)); + + $column1 = new Column('integerfield1', $integerType); + $column2 = new Column('integerfield1', $overriddenStringType); + + $c = new Comparator(); + self::assertEquals([], $c->diffColumn($column1, $column2)); + } + public function testCompareChangedColumnsChangeCustomSchemaOption() { $column1 = new Column('charfield1', Type::getType('string')); diff --git a/tests/Doctrine/Tests/DBAL/Schema/IndexTest.php b/tests/Doctrine/Tests/DBAL/Schema/IndexTest.php index af962f95f95..ad9e2bbbdb7 100644 --- a/tests/Doctrine/Tests/DBAL/Schema/IndexTest.php +++ b/tests/Doctrine/Tests/DBAL/Schema/IndexTest.php @@ -108,6 +108,36 @@ public function testOverrulesWithPartial() self::assertTrue($another->overrules($partial)); } + /** + * @param string[] $columns + * @param int[]|null[] $lengths1 + * @param int[]|null[] $lengths2 + * + * @dataProvider indexLengthProvider + */ + public function testFulfilledWithLength(array $columns, array $lengths1, array $lengths2, bool $expected) : void + { + $index1 = new Index('index1', $columns, false, false, [], ['lengths' => $lengths1]); + $index2 = new Index('index2', $columns, false, false, [], ['lengths' => $lengths2]); + + self::assertSame($expected, $index1->isFullfilledBy($index2)); + self::assertSame($expected, $index2->isFullfilledBy($index1)); + } + + /** + * @return mixed[][] + */ + public static function indexLengthProvider() : iterable + { + return [ + 'empty' => [['column'], [], [], true], + 'same' => [['column'], [64], [64], true], + 'different' => [['column'], [32], [64], false], + 'sparse-different-positions' => [['column1', 'column2'], [0 => 32], [1 => 32], false], + 'sparse-same-positions' => [['column1', 'column2'], [null, 32], [1 => 32], true], + ]; + } + /** * @group DBAL-220 */ diff --git a/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php b/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php index 78fd894de05..8954459eb94 100644 --- a/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Sharding/PoolingShardManagerTest.php @@ -41,6 +41,10 @@ public function testSelectGlobal() { $conn = $this->createConnectionMock(); $conn->expects($this->once())->method('connect')->with($this->equalTo(0)); + $conn->method('getParams') + ->willReturn([ + 'shardChoser' => $this->createMock(ShardChoser::class), + ]); $shardManager = new PoolingShardManager($conn); $shardManager->selectGlobal(); diff --git a/tests/travis/Dockerfile-postgres11 b/tests/travis/Dockerfile-postgres11 deleted file mode 100644 index 039a84a6cfd..00000000000 --- a/tests/travis/Dockerfile-postgres11 +++ /dev/null @@ -1,15 +0,0 @@ -FROM debian:experimental-20180426 - -RUN apt-get update && \ - apt-get install -y -t experimental --no-install-recommends \ - postgresql-11 \ - postgresql-client-11 \ - && \ - rm -rf /var/lib/apt/lists/* - -RUN echo "host all all all trust" >> /etc/postgresql/11/main/pg_hba.conf -RUN echo "listen_addresses='*'" >> /etc/postgresql/11/main/conf.d/listen.conf - -EXPOSE 5432 - -CMD ["sleep", "inf"] diff --git a/tests/travis/install-db2.sh b/tests/travis/install-db2.sh index f5b374db2d3..79c1be98f74 100644 --- a/tests/travis/install-db2.sh +++ b/tests/travis/install-db2.sh @@ -4,19 +4,20 @@ set -ex echo Setting up IBM DB2 -sudo docker pull ibmcom/db2express-c:10.5.0.5-3.10.0 +echo "su - db2inst1 -c 'db2 CONNECT TO doctrine && db2 CREATE USER TEMPORARY TABLESPACE doctrine_tbsp PAGESIZE 4 K'" > /tmp/doctrine-init.sh +chmod +x /tmp/doctrine-init.sh + sudo docker run \ -d \ -p 50000:50000 \ -e DB2INST1_PASSWORD=Doctrine2018 \ -e LICENSE=accept \ + -e DBNAME=doctrine \ + -v /tmp/doctrine-init.sh:/var/custom/doctrine-init.sh:ro \ --name db2 \ - ibmcom/db2express-c:10.5.0.5-3.10.0 \ - db2start - -sleep 15 + --privileged=true \ + ibmcom/db2:11.5.0.0 -sudo docker exec db2 su - db2inst1 -c \ - 'db2 CREATE DB doctrine && db2 CONNECT TO doctrine && db2 CREATE USER TEMPORARY TABLESPACE doctrine_tbsp PAGESIZE 4 K' +sudo docker logs -f db2 | sed '/(*) Setup has completed./ q' echo DB2 started diff --git a/tests/travis/install-mssql-pdo_sqlsrv.sh b/tests/travis/install-mssql-pdo_sqlsrv.sh index 6a82459cc6c..71e07aee231 100644 --- a/tests/travis/install-mssql-pdo_sqlsrv.sh +++ b/tests/travis/install-mssql-pdo_sqlsrv.sh @@ -4,8 +4,4 @@ set -ex echo "Installing extension" -if [ "$TRAVIS_PHP_VERSION" == "7.3" ] || [ "$TRAVIS_PHP_VERSION" == "nightly" ] ; then - pecl install pdo_sqlsrv-5.4.0preview -else - pecl install pdo_sqlsrv -fi +pecl install pdo_sqlsrv-5.7.0preview diff --git a/tests/travis/install-mssql-sqlsrv.sh b/tests/travis/install-mssql-sqlsrv.sh index 18c9453a866..d44841360dc 100644 --- a/tests/travis/install-mssql-sqlsrv.sh +++ b/tests/travis/install-mssql-sqlsrv.sh @@ -4,8 +4,4 @@ set -ex echo "Installing extension" -if [ "$TRAVIS_PHP_VERSION" == "7.3" ] || [ "$TRAVIS_PHP_VERSION" == "nightly" ] ; then - pecl install sqlsrv-5.4.0preview -else - pecl install sqlsrv -fi +pecl install sqlsrv-5.7.0preview diff --git a/tests/travis/install-mysql-5.7.sh b/tests/travis/install-mysql-5.7.sh index 686fa34c991..25459382c6f 100644 --- a/tests/travis/install-mysql-5.7.sh +++ b/tests/travis/install-mysql-5.7.sh @@ -2,21 +2,14 @@ set -ex -echo "Installing MySQL 5.7..." +echo "Starting MySQL 5.7..." -sudo service mysql stop -sudo apt-get remove "^mysql.*" -sudo apt-get autoremove -sudo apt-get autoclean -echo mysql-apt-config mysql-apt-config/select-server select mysql-5.7 | sudo debconf-set-selections -wget http://dev.mysql.com/get/mysql-apt-config_0.8.9-1_all.deb -sudo DEBIAN_FRONTEND=noninteractive dpkg -i mysql-apt-config_0.8.9-1_all.deb -sudo rm -rf /var/lib/apt/lists/* -sudo apt-get clean -sudo apt-get update -q -sudo apt-get install -q -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" mysql-server libmysqlclient-dev -sudo mysql_upgrade - -echo "Restart mysql..." -sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('') where User='root'; update user set plugin='mysql_native_password';FLUSH PRIVILEGES;" +sudo docker run \ + -d \ + -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \ + -e MYSQL_DATABASE=doctrine_tests \ + -p 33306:3306 \ + --name mysql57 \ + mysql:5.7 +sudo docker exec -i mysql57 bash <<< 'until echo \\q | mysql doctrine_tests > /dev/null 2>&1 ; do sleep 1; done' diff --git a/tests/travis/install-postgres-11.sh b/tests/travis/install-postgres-11.sh index 9137a55001b..2ef1aabc4f0 100644 --- a/tests/travis/install-postgres-11.sh +++ b/tests/travis/install-postgres-11.sh @@ -6,9 +6,7 @@ echo "Preparing Postgres 11" sudo service postgresql stop || true -sudo docker build -t postgres11 - < tests/travis/Dockerfile-postgres11 -sudo docker run -d --name postgres11 -p 5432:5432 postgres11 -sudo docker exec postgres11 service postgresql start -sudo docker exec -i postgres11 su -c psql postgres <<<"create database doctrine_tests" +sudo docker run -d --name postgres11 -p 5432:5432 postgres:11.1 +sudo docker exec -i postgres11 bash <<< 'until pg_isready -U postgres > /dev/null 2>&1 ; do sleep 1; done' echo "Postgres 11 ready" diff --git a/tests/travis/mysql.docker.travis.xml b/tests/travis/mysql.docker.travis.xml new file mode 100644 index 00000000000..21fae6f35e9 --- /dev/null +++ b/tests/travis/mysql.docker.travis.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + ../Doctrine/Tests/DBAL + + + + + + ../../lib/Doctrine + + + + + + + + + + performance + locking_functional + + + diff --git a/tests/travis/mysqli.docker.travis.xml b/tests/travis/mysqli.docker.travis.xml new file mode 100644 index 00000000000..935815e9cf4 --- /dev/null +++ b/tests/travis/mysqli.docker.travis.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + ../Doctrine/Tests/DBAL + + + + + + ../../lib/Doctrine + + + + + + + + + + performance + locking_functional + + +