From 9d382e6968e6e60de75db9ce304f73e080201cc8 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Mon, 3 Dec 2018 20:46:22 -0800 Subject: [PATCH 01/34] Bump version to 2.9.1-DEV --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index 570f56afba0..f643d14f1b3 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.1-DEV'; /** * Compares a Doctrine version with the current one. From bf794a926312f8134b3262d9aec2a3bafd466235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steve=20M=C3=BCller?= Date: Mon, 3 Dec 2018 15:57:33 +0100 Subject: [PATCH 02/34] fix bound parameter references on SQL Anywhere and PHP 7 --- .../DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index 0ed2d454624..0bdd9a93c95 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); } From 9a2be238d47ffcfa3448909aeccf601606d90f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steve=20M=C3=BCller?= Date: Tue, 4 Dec 2018 10:04:27 +0100 Subject: [PATCH 03/34] fix query limit values "0" and "null" on SQL Anywhere --- .../DBAL/Platforms/SQLAnywherePlatform.php | 23 ++++++++----------- .../Platforms/SQLAnywherePlatformTest.php | 10 +++++++- 2 files changed, 18 insertions(+), 15 deletions(-) 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/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()); From d3d936e43bfd3ae579491fa63a21cb1bd89598e3 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Tue, 4 Dec 2018 09:26:17 -0800 Subject: [PATCH 04/34] Reverted strict comparison back to loose because of a new regression --- lib/Doctrine/DBAL/Schema/Comparator.php | 2 +- phpcs.xml.dist | 5 +++++ .../DBAL/Functional/Schema/ComparatorTest.php | 22 ++++++++++++++++--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/Comparator.php b/lib/Doctrine/DBAL/Schema/Comparator.php index 3b5a0b155d0..1d8a7275d98 100644 --- a/lib/Doctrine/DBAL/Schema/Comparator.php +++ b/lib/Doctrine/DBAL/Schema/Comparator.php @@ -435,7 +435,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/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/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], + ]; + } } From 1a9650844d41be5c3443baed9923c178b57c978f Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Tue, 4 Dec 2018 09:52:32 -0800 Subject: [PATCH 05/34] Fixed boolean column detection on Oracle --- .../DBAL/Schema/OracleSchemaManager.php | 66 +++++-------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php b/lib/Doctrine/DBAL/Schema/OracleSchemaManager.php index e84b0a551f8..250ec2a2652 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 = [ From 94078fda1044ee533b4ce208b17120972c250764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steve=20M=C3=BCller?= Date: Wed, 5 Dec 2018 21:14:27 +0100 Subject: [PATCH 06/34] fix fetching empty values via fetchAll() on SQL Anywhere --- .../DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php index 0bdd9a93c95..6354836a478 100644 --- a/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php +++ b/lib/Doctrine/DBAL/Driver/SQLAnywhere/SQLAnywhereStatement.php @@ -253,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; } } From 2a4ae2c37fee6bb01ae08116c76deec410325420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steve=20M=C3=BCller?= Date: Wed, 5 Dec 2018 21:31:11 +0100 Subject: [PATCH 07/34] fix view schema introspection test on SQL Anywhere --- .../DBAL/Functional/Schema/SQLAnywhereSchemaManagerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() From f6dd1344178f8f273b0cb07fd95ab55d688ac14c Mon Sep 17 00:00:00 2001 From: Marc-Jan Barnhoorn Date: Thu, 6 Dec 2018 17:20:46 -0800 Subject: [PATCH 08/34] Fix Fetch mode query in MasterSlaveConnection --- .../Connections/MasterSlaveConnection.php | 2 + .../Functional/MasterSlaveConnectionTest.php | 51 +++++++++++++++++++ 2 files changed, 53 insertions(+) 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/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']); + } } From 79e369e8867d627dbf7e413cda1a061b0cbaa938 Mon Sep 17 00:00:00 2001 From: "roger.codina" Date: Tue, 4 Dec 2018 18:23:21 +0100 Subject: [PATCH 09/34] ResultCacheStatement lost its cache capability on fetchAll method --- lib/Doctrine/DBAL/Cache/ResultCacheStatement.php | 5 ++++- .../Doctrine/Tests/DBAL/Functional/ResultCacheTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 872f3e71fc9..721709a0dd4 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -166,7 +166,10 @@ 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); + $this->data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + $this->emptied = true; + + return $this->data; } /** diff --git a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php index ae4259da91f..2c64c280b6d 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php @@ -166,6 +166,16 @@ 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 assertCacheNonCacheSelectSameFetchModeAreEqual($expectedResult, $fetchMode) { $stmt = $this->connection->executeQuery('SELECT * FROM caching ORDER BY test_int ASC', [], [], new QueryCacheProfile(10, 'testcachekey')); From b18f63ccbfb3bd99a59fb67a900991f0c478c2f3 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 6 Dec 2018 17:57:07 -0800 Subject: [PATCH 10/34] Fixed parsing MySQL create table flags (options without a value) --- .../DBAL/Schema/MySqlSchemaManager.php | 30 ++++++++++--------- .../Schema/MySqlSchemaManagerTest.php | 6 +++- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index b6332026cdd..2c058b8bf6a 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. @@ -305,25 +304,28 @@ public function listTableDetails($tableName) $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 === '') { + 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/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 475dcee3ee6..937d04c9058 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -502,6 +502,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 +512,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 From b84603b9916899ff56914bf574262cf56c997e84 Mon Sep 17 00:00:00 2001 From: Lukas Vitek Date: Thu, 6 Dec 2018 20:25:12 +0100 Subject: [PATCH 11/34] Fixed annotation and name of parameter $columnsNames in Table --- lib/Doctrine/DBAL/Schema/Table.php | 38 +++++++++++++----------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 5dac5bb1a34..191294d6d15 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; @@ -100,16 +98,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 +116,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 +166,7 @@ public function dropIndex($indexName) } /** - * @param mixed[][] $columnNames + * @param string[] $columnNames * @param string|null $indexName * @param mixed[] $options * @@ -236,15 +234,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 +251,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 +268,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); } From f3711a7506f09788373ea83a3c980eb0c3777f72 Mon Sep 17 00:00:00 2001 From: "Jonathan H. Wage" Date: Wed, 12 Dec 2018 13:47:45 -0600 Subject: [PATCH 12/34] Merge pull request #3404 from greg0ire/update_chat_link Link to Slack instead of Gitter --- docs/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 `_. From ec74d6e300d78fbc896669c3ca57ef9719adc9c6 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 13 Dec 2018 20:51:13 -0800 Subject: [PATCH 13/34] Release v2.9.1 --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index f643d14f1b3..52802c38f0a 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.1-DEV'; + public const VERSION = '2.9.1'; /** * Compares a Doctrine version with the current one. From dac7d6077325c8140241ee27aee3b3bfbe285ba3 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 13 Dec 2018 20:55:54 -0800 Subject: [PATCH 14/34] Bump version to 2.9.2-DEV --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index 52802c38f0a..2d44daee8e1 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.1'; + public const VERSION = '2.9.2-DEV'; /** * Compares a Doctrine version with the current one. From b8c5bee2f6b55101e835100fcab6dfc6a05b4172 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 16 Dec 2018 23:25:32 -0800 Subject: [PATCH 15/34] `CREATE_OPTIONS` is nullable in MySQL --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 4 ++-- .../DBAL/Functional/Schema/MySqlSchemaManagerTest.php | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 2c058b8bf6a..7bcd5533efc 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -312,11 +312,11 @@ public function listTableDetails($tableName) /** * @return string[]|true[] */ - private function parseCreateOptions(string $string) : array + private function parseCreateOptions(?string $string) : array { $options = []; - if ($string === '') { + if ($string === null || $string === '') { return $options; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 937d04c9058..90ade2e86b8 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php @@ -531,4 +531,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')); + } } From d807849f95d454ca39c4b83b4aaddc3fac4f5593 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Tue, 18 Dec 2018 20:23:57 -0800 Subject: [PATCH 16/34] Consider column lengths when comparing indices --- lib/Doctrine/DBAL/Schema/Index.php | 18 +++++++++++ .../Doctrine/Tests/DBAL/Schema/IndexTest.php | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+) 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/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 */ From 22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 30 Dec 2018 19:27:51 -0800 Subject: [PATCH 17/34] Release v2.9.2 --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index 2d44daee8e1..c6e44a045e4 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.2-DEV'; + public const VERSION = '2.9.2'; /** * Compares a Doctrine version with the current one. From f1926101d20ae43011ae84bc198c822bc887ba99 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 30 Dec 2018 19:58:04 -0800 Subject: [PATCH 18/34] Bump version to 2.9.3-DEV --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index c6e44a045e4..eb852a273cd 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.2'; + public const VERSION = '2.9.3-DEV'; /** * Compares a Doctrine version with the current one. From 5fe8b7952c6c5bfc4d53069b4eb4176ea1ad24b0 Mon Sep 17 00:00:00 2001 From: Leo Feyer Date: Wed, 2 Jan 2019 14:41:37 +0100 Subject: [PATCH 19/34] Cast the index length to int in the MySqlSchemaManager class --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 2 +- .../Functional/Schema/MySqlSchemaManagerTest.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 7bcd5533efc..29f019ac363 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -68,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; } diff --git a/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php b/tests/Doctrine/Tests/DBAL/Functional/Schema/MySqlSchemaManagerTest.php index 90ade2e86b8..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 */ From 70b9939eb65a9a3081db56fdd5d4406a0119cab4 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 7 Feb 2019 20:29:20 -0800 Subject: [PATCH 20/34] Fixed mistakenly passing builds on AppVeyor PowerShell doesn't automatically handle exit codes of the invoked commands automatically, and $ErrorActionPreference = "Stop" doesn't seem working. Additionally, the ocular command is moved out of the "test_script" section to "after_test" since it's not part of the actual test. --- .appveyor.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 + } From db124dbccb615628d4fe494ca45300586dbfabe6 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 3 Feb 2019 09:12:37 -0800 Subject: [PATCH 21/34] Replaced custom docker image for PostgreSQL with the official one See https://travis-ci.org/doctrine/dbal/jobs/488051004 for the dependency issue. --- tests/travis/Dockerfile-postgres11 | 15 --------------- tests/travis/install-postgres-11.sh | 6 ++---- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 tests/travis/Dockerfile-postgres11 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-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" From b0e683498945c6ddb793b611faeea9ab884899f2 Mon Sep 17 00:00:00 2001 From: Michael Moravec Date: Sat, 9 Feb 2019 17:51:01 +0100 Subject: [PATCH 22/34] CI: Test against PHP 7.4snapshot instead of nightly (8.0) --- .travis.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6be26170ae5..def8fffbfc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ after_script: jobs: allow_failures: - - php: nightly + - php: 7.4snapshot include: - stage: Test @@ -319,31 +319,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 +351,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 +363,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: From ed759f548a1a4d40f8c1d020ed3cc5917bc86c30 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Mon, 20 May 2019 13:25:18 -0700 Subject: [PATCH 23/34] Default column expressions do not work on SQL Server --- .../DBAL/Platforms/SQLServerPlatform.php | 31 -------- .../Platform/DefaultExpressionTest.php | 78 +++++++++++++++++++ .../Schema/SQLServerSchemaManagerTest.php | 17 +--- .../AbstractSQLServerPlatformTestCase.php | 2 +- 4 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 tests/Doctrine/Tests/DBAL/Functional/Platform/DefaultExpressionTest.php 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/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/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) ); } From bafa923c2471715912020cd70de2795648c304d4 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Wed, 20 Feb 2019 10:16:53 -0800 Subject: [PATCH 24/34] Replaced MySQL 5.7 installed from a PPA with an official Docker image See https://travis-ci.org/doctrine/dbal/jobs/495144274 for the PPA issue. --- .travis.yml | 4 ++-- tests/travis/install-mysql-5.7.sh | 25 +++++++++---------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index def8fffbfc6..c6b21cd1192 100644 --- a/.travis.yml +++ b/.travis.yml @@ -117,7 +117,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 +133,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 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' From 790ef4fcac55f03c69e9b0d859535fb23f21968c Mon Sep 17 00:00:00 2001 From: Michael Moravec Date: Thu, 14 Jun 2018 23:33:13 +0200 Subject: [PATCH 25/34] Borrowed MySQL in Docker configuration from #3407 --- tests/travis/mysql.docker.travis.xml | 49 +++++++++++++++++++++++++++ tests/travis/mysqli.docker.travis.xml | 49 +++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 tests/travis/mysql.docker.travis.xml create mode 100644 tests/travis/mysqli.docker.travis.xml 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 + + + From 68e1ffa5bd74ee7d58d95782388ae98dba93416f Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 13 Dec 2018 21:58:29 -0800 Subject: [PATCH 26/34] Fixed test failures in case of a non-standard MySQL port --- .../Tests/DBAL/Functional/Driver/Mysqli/ConnectionTest.php | 1 + 1 file changed, 1 insertion(+) 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'], From 212ac4dcbef8afa406e5a97c50e8d3ea57a9ea81 Mon Sep 17 00:00:00 2001 From: garret-gunter Date: Mon, 17 Jun 2019 12:13:23 -0600 Subject: [PATCH 27/34] Compare type class when comparing columns. --- lib/Doctrine/DBAL/Schema/Comparator.php | 7 ++++- .../Tests/DBAL/Schema/ComparatorTest.php | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Schema/Comparator.php b/lib/Doctrine/DBAL/Schema/Comparator.php index 1d8a7275d98..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; } 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')); From 08dca0d3de79a267691c9b2ad51114d76a4456fc Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Mon, 8 Jul 2019 05:55:22 +0300 Subject: [PATCH 28/34] Fixed query result caching when FetchMode::COLUMN is used Internally, ArrayStatement expects every row to be represented as an array regardless of the fetch mode, however FetchMode::COLUMN produces one value per row. --- lib/Doctrine/DBAL/Cache/ResultCacheStatement.php | 10 +++++++++- .../Tests/DBAL/Functional/ResultCacheTest.php | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php index 721709a0dd4..35e7c986bc6 100644 --- a/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php +++ b/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php @@ -166,7 +166,15 @@ public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEX */ public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) { - $this->data = $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/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php index 2c64c280b6d..caf90927c3b 100644 --- a/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php +++ b/tests/Doctrine/Tests/DBAL/Functional/ResultCacheTest.php @@ -176,6 +176,22 @@ public function testFetchAllAndFinishSavesCache() 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')); From 367f86e8ff96b8d5467aae812f95cab45f5d9c09 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Fri, 23 Aug 2019 09:10:27 -0700 Subject: [PATCH 29/34] Marked connection exception test incomplete on MySQL 8 --- .../Tests/DBAL/Functional/ExceptionTest.php | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) 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); From a9b7bf855c6f391f96773f844d7158500394bcef Mon Sep 17 00:00:00 2001 From: Konstantin Kalinin Date: Fri, 20 Sep 2019 12:59:32 +0300 Subject: [PATCH 30/34] Reset transaction nesting level on connection loss. When the connection is lost or is closed, subsequent transaction will no longer be nested because they started in a brand new session. Our internal representation of the nesting shold take this into account --- lib/Doctrine/DBAL/Connection.php | 2 ++ .../Tests/DBAL/Functional/ConnectionTest.php | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+) 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/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() From cd36b047a0ae3c334c8d6f31abca8d6972c14306 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sun, 7 Jul 2019 05:31:37 +0300 Subject: [PATCH 31/34] Switched from ibmcom/db2express-c to ibmcom/db2 --- tests/travis/install-db2.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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 From 61b652d98d817e420ee93f47e069ec4dbd735171 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sat, 3 Aug 2019 09:27:57 -0700 Subject: [PATCH 32/34] Fixed test failures on PHP 7.4 The failures are caused by the changes in PHP according to https://wiki.php.net/rfc/notice-for-non-valid-array-container. --- lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php | 7 +++++++ lib/Doctrine/DBAL/Schema/Table.php | 6 ++++-- .../Doctrine/Tests/DBAL/Driver/OCI8/OCI8StatementTest.php | 5 +++++ .../Tests/DBAL/Sharding/PoolingShardManagerTest.php | 4 ++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 29f019ac363..c27e8308845 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -296,13 +296,20 @@ 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'])); diff --git a/lib/Doctrine/DBAL/Schema/Table.php b/lib/Doctrine/DBAL/Schema/Table.php index 191294d6d15..c89298bb015 100644 --- a/lib/Doctrine/DBAL/Schema/Table.php +++ b/lib/Doctrine/DBAL/Schema/Table.php @@ -37,7 +37,9 @@ class Table extends AbstractAsset protected $_fkConstraints = []; /** @var mixed[] */ - protected $_options = []; + protected $_options = [ + 'create_options' => [], + ]; /** @var SchemaConfig|null */ protected $_schemaConfig = null; @@ -72,7 +74,7 @@ public function __construct($tableName, array $columns = [], array $indexes = [] $this->_addForeignKeyConstraint($constraint); } - $this->_options = $options; + $this->_options = array_merge($this->_options, $options); } /** 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/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(); From 5ac70f6127090409c439605b6784bdbb0d329a70 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Thu, 17 Oct 2019 09:28:26 -0700 Subject: [PATCH 33/34] Updated SQL Server extensions to fix build failures on PHP 7.4 --- .travis.yml | 3 --- tests/travis/install-mssql-pdo_sqlsrv.sh | 6 +----- tests/travis/install-mssql-sqlsrv.sh | 6 +----- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index c6b21cd1192..40598c3374b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,9 +41,6 @@ after_script: fi jobs: - allow_failures: - - php: 7.4snapshot - include: - stage: Test php: 7.1 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 From 7345cd59edfa2036eb0fa4264b77ae2576842035 Mon Sep 17 00:00:00 2001 From: Sergei Morozov Date: Sat, 2 Nov 2019 15:19:34 -0700 Subject: [PATCH 34/34] Release v2.9.3 --- lib/Doctrine/DBAL/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/DBAL/Version.php b/lib/Doctrine/DBAL/Version.php index eb852a273cd..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.3-DEV'; + public const VERSION = '2.9.3'; /** * Compares a Doctrine version with the current one.