Skip to content

Commit

Permalink
Fix(migrations) Handle transaction within migrations
Browse files Browse the repository at this point in the history
This fixes #98
  • Loading branch information
thePanz committed Jun 27, 2024
1 parent f47a790 commit 6600ed5
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 28 deletions.
20 changes: 16 additions & 4 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ jobs:
matrix:
php-version:
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"
#- "8.0"
#- "8.1"
#- "8.2"
#- "8.3"
services:
mysql:
image: mysql:5.7-debian
env:
MYSQL_DATABASE: 'doctrine1_test'
MYSQL_USER: 'doctrine1'
MYSQL_PASSWORD: 'd0ctr1n3'
MYSQL_ALLOW_EMPTY_PASSWORD: true
ports:
- 3306:3306

steps:
- name: Checkout
Expand Down Expand Up @@ -62,4 +72,6 @@ jobs:
run: composer install --prefer-dist

- name: Run Tests
env:
MYSQL_TEST_DSN: 'mysql:host=localhost;dbname=doctrine1_test;user=doctrine1;password=d0ctr1n3'
run: cd tests && php run.php
2 changes: 1 addition & 1 deletion lib/Doctrine/Export.php
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,7 @@ public function exportClasses(array $classes)
}
}

$connection->commit();
Doctrine_TransactionHelper::commitIfInTransaction($connection);
}
}

Expand Down
42 changes: 19 additions & 23 deletions lib/Doctrine/Migration.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public function loadMigrationClass($name, $path = null)
}

if ($class === false) {
return false;
return;
}

if (empty($this->_migrationClasses)) {
Expand Down Expand Up @@ -307,15 +307,13 @@ public function getNextMigrationClassVersion()
*
* @param integer $to Version to migrate to
* @param boolean $dryRun Whether or not to run the migrate process as a dry run
* @return integer $to Version number migrated to
* @return bool True if the migration succeeded, false otherwise
* @throws Doctrine_Exception
*/
public function migrate($to = null, $dryRun = false)
{
$this->clearErrors();

$this->_createMigrationTable();

$this->_connection->beginTransaction();

try {
Expand All @@ -335,24 +333,21 @@ public function migrate($to = null, $dryRun = false)

if ($dryRun) {
return false;
} else {
$this->_throwErrorsException();
}
} else {
if ($dryRun) {
$this->_connection->rollback();
if ($this->hasErrors()) {
return false;
} else {
return $to;
}
} else {
$this->_connection->commit();
$this->setCurrentVersion($to);
return $to;
}

$this->_throwErrorsException();
}
return false;

if ($dryRun) {
$this->_connection->rollback();

return !$this->hasErrors();
}

Doctrine_TransactionHelper::commitIfInTransaction($this->_connection);
$this->setCurrentVersion($to);

return true;
}

/**
Expand Down Expand Up @@ -437,8 +432,9 @@ public function getMigrationClass($num)
/**
* Throw an exception with all the errors trigged during the migration
*
* @return void
* @throws Doctrine_Migration_Exception $e
* @throws Doctrine_Migration_Exception
*
* @return never
*/
protected function _throwErrorsException()
{
Expand Down Expand Up @@ -559,4 +555,4 @@ protected function _createMigrationTable()
return false;
}
}
}
}
24 changes: 24 additions & 0 deletions lib/Doctrine/TransactionHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

/**
* Helper for transactions handling.
*
* @author Kyle McGrogan <[email protected]>
*/
final class Doctrine_TransactionHelper
{
/**
* Execute a commit on the given connection, only if a transaction already started.
*/
public static function commitIfInTransaction(Doctrine_Connection $connection): void
{
$handler = $connection->getDbh();

// Attempt to commit while no transaction is running results in exception since PHP 8 + pdo_mysql combination
if ($handler instanceof PDO && !$handler->inTransaction()) {
return;
}

$connection->commit();
}
}
13 changes: 13 additions & 0 deletions tests/DoctrineTest/Doctrine_UnitTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ public function init()
}
}
}

public function prepareTables() {
foreach($this->tables as $name) {
$name = ucwords($name);
Expand All @@ -230,6 +231,7 @@ public function prepareTables() {
$this->conn->export->exportClasses($this->tables);
$this->objTable = $this->connection->getTable('User');
}

public function prepareData()
{
$groups = new Doctrine_Collection($this->connection->getTable('Group'));
Expand Down Expand Up @@ -307,6 +309,17 @@ public function getDeclaration($type)
return $this->dataDict->getPortableDeclaration(array('type' => $type, 'name' => 'colname', 'length' => 1, 'fixed' => true));
}

/**
* @param string $pdoDsn
* @return Doctrine_Connection
*/
protected function openAdditionalPdoConnection(string $pdoDsn)
{
$pdoFromDsn = new PDO($pdoDsn);

return $this->openAdditionalConnection($pdoFromDsn);
}

protected function openAdditionalConnection($adapter = null, $name = null)
{
$connection = $this->manager->openConnection($adapter, $name);
Expand Down
68 changes: 68 additions & 0 deletions tests/MigrationMysqlTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/

/**
* Doctrine_Migration_TestCase
*
* @package Doctrine
* @author Konsta Vesterinen <[email protected]>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @category Object Relational Mapping
* @link www.doctrine-project.org
* @since 1.0
* @version $Revision$
*/
class Doctrine_MigrationMysql_TestCase extends Doctrine_UnitTestCase
{
public function setUp()
{
parent::setUp();
$dsn = getenv('MYSQL_TEST_DSN') ?? '';

$this->connection = $this->openAdditionalPdoConnection($dsn);
$this->connection->setOption('dsn', $dsn);
$this->connection->dropDatabase();
$this->connection->createDatabase();
}

public function testAfterSuccessfullMigrationItWillSetMigratedVersionAsCurrentVersionInMysqlDB()
{
$migration = new Doctrine_Migration(__DIR__.'/migration_classes', $this->connection);
$this->assertFalse($migration->hasMigrated());
$migration->setCurrentVersion(0);
$this->assertFalse($this->connection->import->tableExists('migration_phonenumber'));
$this->assertFalse($this->connection->import->tableExists('migration_user'));
$this->assertFalse($this->connection->import->tableExists('migration_profile'));

$migration->migrate(3);
$this->assertTrue($migration->hasMigrated());
$this->assertEqual($migration->getCurrentVersion(), 3);
$this->assertTrue($this->connection->import->tableExists('migration_phonenumber'));
$this->assertTrue($this->connection->import->tableExists('migration_user'));
$this->assertTrue($this->connection->import->tableExists('migration_profile'));

$migration->migrate(4);
$this->assertEqual($migration->getCurrentVersion(), 4);
$this->assertTrue($this->connection->import->tableExists('migration_phonenumber'));
$this->assertTrue($this->connection->import->tableExists('migration_user'));
$this->assertFalse($this->connection->import->tableExists('migration_profile'));
}
}
1 change: 1 addition & 0 deletions tests/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@
// Migration Tests
$migration = new GroupTest('Migration Tests', 'migration');
$migration->addTestCase(new Doctrine_Migration_TestCase());
$migration->addTestCase(new Doctrine_MigrationMysql_TestCase());
$migration->addTestCase(new Doctrine_Migration_Base_TestCase());
$migration->addTestCase(new Doctrine_Migration_Diff_TestCase());
$test->addTestCase($migration);
Expand Down

0 comments on commit 6600ed5

Please sign in to comment.