migrations: DDL migrations produce errors on PHP 8.0 + MySQL in transactional mode
BC Break Report
Q | A |
---|---|
BC Break | yes |
Version | 3.0.2 (probably > 3.0) |
PHP | 8.0 |
DBAL-Driver | PDO |
DB | mysql 5.7 and 8.0 |
Summary
When running the command migrations:migrate
with a ddl sql statement (e.g. create table
) on php 8.0 I got an error:
In Connection.php line 1761: There is no active transaction
Previous behavior
Bevore php 8.0 the migration produces no errors.
Current behavior
DDL statements on mysql result in an implicit commit (see mysql documentation). This causes the explicit commit in transactional mode to fail. But only starting with php 8.0 an error is raised (probably due to php/php-src@990bb34).
To bypass the error I can disable the transactional mode by overwriting isTransactional
in my migration, but in my opinion it’s a breaking change in the behaviour when using php 8.
I found the same issue in a bug report for the yii framework (yiisoft/yii2#18406) that helped me to figure out what happens. They fixed it by checking PDO::inTransaction
before running the commit. If I’m not wrong this method is not wrapped by the dbal package and therefore not available here.
How to reproduce
- Create a migration with a ddl statement.
- Let the transactional mode active.
- Run the migration.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 73
- Comments: 80 (39 by maintainers)
Commits related to this issue
- Workaround for Doctrine Migrations See https://github.com/doctrine/migrations/issues/1104 — committed to archlinux-de/www.archlinux.de by pierres 4 years ago
- Workaround missing PDO transaction on php8.0 https://github.com/doctrine/migrations/issues/1104 — committed to roadiz/roadiz by ambroisemaupate 3 years ago
- fix: doctrine bug - disabled transaction mode in migrations (https://github.com/doctrine/migrations/issues/1104) — committed to SBordier44/symfony5-ecommerce by SBordier44 3 years ago
- Feature/products (#48) * feat: products - Entity,fixtures and eventListeners * fix: doctrine bug - disabled transaction mode in migrations (https://github.com/doctrine/migrations/issues/1104) *... — committed to SBordier44/symfony5-ecommerce by SBordier44 3 years ago
- Temporary fix for - https://github.com/doctrine/migrations/issues/1104 — committed to tarlepp/symfony-flex-backend by tarlepp 3 years ago
- Workout for doctrine/migrations#1104 — committed to baupen/web by famoser 3 years ago
- Workout for doctrine/migrations#1104 — committed to baupen/web by famoser 3 years ago
- Workaround for doctrine/migrations#1104 — committed to baupen/web by famoser 3 years ago
- Temporary fix for https://github.com/doctrine/migrations/issues/1104 — committed to netgen-layouts/layouts-core by emodric 3 years ago
- fix: workaround for doctrine/migrations#1104 With PHP 8.0 doctrine migrations now errors when there is only DDL in the migration with a "There is no active transaction" message from Connection.php li... — committed to opensalt/opensalt by roverwolf 3 years ago
- Fix: https://github.com/doctrine/migrations/issues/1104 — committed to SpyWSamara/symfony-the-fast-track by SpyWSamara 3 years ago
- Fix: https://github.com/doctrine/migrations/issues/1104 — committed to SpyWSamara/symfony-the-fast-track by SpyWSamara 3 years ago
- Fix migrations after Doctrine update Related: https://github.com/doctrine/migrations/issues/1104 — committed to utmsigep/member-directory by stephenyeargin 3 years ago
- fix: Dirty doctrine migrations issue Fixes https://github.com/doctrine/migrations/issues/1104 — committed to nplhse/cois by nplhse 3 years ago
- bugfix for https://github.com/doctrine/migrations/issues/1104 — committed to developeregrem/fewohbee by deleted user 3 years ago
- Correctif des migrations avec PHP 8 (erreur There is no active transaction : https://github.com/doctrine/migrations/issues/1104). — committed to steve-caillault/nascar-symfony by steve-caillault 3 years ago
Just do it like pierres as a workaround until its fixed. Add following code into your migrations.
This works for me,
downgrade
doctrine/doctrine-migrations-bundle
from3.2.0
to3.1.1
anddoctrine/migrations
from3.3.0
to3.2.1
on php8 & MySQL 8.0.20
So AFAIS up to PHP 7.4 such a migration will execute roughly as if it were
isTransactional() == false
(only roughly because everything up to the first DDL will be within a transaction), because the failing commit was silently ignored. In PHP8 however the manual commit will now throw a PDOException, which totally breaks the transaction and stops following migrations in the execution plan from running. This is a major PITA for all projects using doctrine migrations and trying to support PHP 8, as it can not easily be handled by them automatically (catching the exception there will still have later migrations not executed). This means all existing migrations need to be changed and own users need to be advised to change their migrations.In this case, I’m with the OP and the right solution IMO would be to create a BUGFIX that catches this very specific PDOException and ignores it to create b/c for now. Then a “sane” solution to solve this through proper API can be discussed with all the time needed.
Is this an anti-pattern? Pretty sure. Would I want to do something like this regularly? Nope. Is this solving a pain? Yep. Dirty as heck, but it is what it is.
Keep in mind that a migration does not necessarily contain DDL statements. A migration that only consists of
INSERT
andUPDATE
statements can very well be transactional.Hi! The easiest solution is to provide needed version in composer.json
"doctrine/doctrine-migrations-bundle":"3.1.1", "doctrine/migrations": "3.2.1",
Transactions works correctly with php 8.0 + MySql 5.7
See linked issues and pull requests. I don’t know why do you guys comment on closed issue…
And I have opened https://github.com/doctrine/migrations/pull/1131 for fixing PHP 8.0 + MySQL issue.
It’s ugly, but I like it! 👍
Following the doc : https://www.doctrine-project.org/projects/doctrine-migrations/en/3.3/reference/configuration.html Disable globally
transactional
by setting it tofalse
for migrations solve that case.If you are using Symfony and
doctrine/doctrine-migrations-bundle
3.2.x
, follow the doc : https://symfony.com/bundles/DoctrineMigrationsBundle/current/index.html To disabletransactional
fromconfig/packages/doctrine_migrations.yaml
Link to the 3.1 PR in case anyone is interested: https://github.com/doctrine/dbal/pull/4481
I would propose to have
AbstractMigration::isTransactional()
defaulting to$platform->supportsTransactionalDDL()
(adding that method in DBAL if it does not exist yet).For platforms supporting transactional DDL, this is the best default (if you make a mistake in your migration, it avoids having to clean a state of a half-executed migration). But default to always transactional indeed does not look good anymore on mysql due to the magic it does…
@digitalkaoz what fix? There is no fix, just don’t attempt to use DDL + transactions with MySQL or Oracle, they do not support it.
@ostrolucky I wonder if you actually read the comments before making your remark. Because if you did, you would understand that ‘linked issues and pull requests’ had nothing to do with the problem discussed. Oh and probably you would also understand why we comment on a closed issue.
any fixes for php 8? weird things but
all_or_nothing
doesn’t help still usingisTransactional()
return falseSame setup, can confirm, this works
I’d rather not upgrade to 3 yet, it’s not perfect when using multiple entityManagers (at least i haven’t found a good way to make it work yet). But your solution might work for others. Thanks either way.
I had also updated doctrine/migrations and doctrine/doctrine-migrations-bundle to their latest versions, but started to receive this error again. Turned out that it was caused by a
Connection
wrapper which isn’t handled correctly byDoctrine/Migrations/Tools/TransactionHelper.php
. See https://github.com/getsentry/sentry-symfony/issues/488, https://github.com/doctrine/migrations/pull/1149 and https://github.com/doctrine/dbal/issues/4616.@yyaremenko my bad this time 😃 I’ve meant
doctrine/doctrine-migrations-bundle:^3.1.1
. As of this moment, this should installdoctrine/migrations:3.1.2
.@yyaremenko blindly recommending to remove composer.lock could lead to issues with other packages that get upgraded. Why not
composer require doctrine/migrations:^3.1.1
?I see. If we are going to do this I think we should also fix this paragraph of the docs. We should probably add a warning similar to this one (but in proper english 😉), and tell people to refer to the docs of their RDBMS to know what might actually be happening.
Yeah one thing after another. People were delusioned for a long long time that they get some kind of transaction protection during DDL statements in MySQL, this is not the right ticket to solve it in. By the way, Oracle also doesn’t support it and possibly other databases. For now we just need to make migrations work with MySQL again. That’s why I plan to submit a patch today or tomorrow according solution in https://github.com/doctrine/migrations/issues/1104#issuecomment-786311346 so we could already have fix which makes migrations on PHP 8 with most popular RDBMS work again.
Given the answer you got here, I’m starting to have some doubts about this.
Regarding https://github.com/doctrine/migrations/issues/1104#issuecomment-786311346 , it has the merit of not being coupled at all to the DBAL, unlike
instanceof MySQLPlatform
, so there’s that. What’s very wrong with it is that it hides the fact that the migration is not wrapped in a transaction to our users and that despite the factisTransactional()
returnstrue
. That has always been the case, and it’s only surfacing now that an exception is thrown, but that kind of shushing sure doesn’t feel right.Maybe it should be part of the configuration? Meaning possible values for
transactional_by_default
could become:true
tentatively
ormostly
orsilence_failures
false
That way people that use MySQL + PDO or Oracle or whatever doesn’t work can have migrations that works without deluding themselves. We could even print a message in the output indicating whether the migration ended up being transactional or not in this kind of case.
Another workaround is to switch to
mysqli
driver instead ofpdo_mysql
, as onlypdo_mysql
triggers this exception, as can be seen in https://github.com/doctrine/dbal/pull/4544@morozov I am not sure the DBAL could solve the issue, but it would definitely help to have
inTransaction()
added to the API ofConnection
. For systems that don’t have an equivalent toPDO::inTransaction()
, it could still be implemented by doing some bookkeeping regarding the number of calls tobeginTransaction()
,commit()
androllBack()
.In v2, this package has 4 occurrences of
commit()
, all of which probably need to be complexified to account for this MySQL “feature”. Also, there are 4 occurrences ofrollBack()
, and surely, callingrollBack()
if there is no active transaction would fail, e.g. in this (unlikely, I concede that) scenario:Alternate solution to adding
inTransaction
to the API would be to add an argument tocommit
androllBack
that would make the check toinTransaction
implicit, or to add a new API likemaybeCommit
/maybeRollback
.All of my proposals would constitue BC-breaks in the DBAL though, so we would still have to complexify code here as a first step I’m afraid.
Also, none of them feel really good to me, maybe you will have better ideas?
Yes,
instanceof \PDO
is also working, just checked it. I think this approach is better.