-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add test case for successful transactional DDL #4544
Conversation
Based on conversation at doctrine#4481 it's undesired that consumer should have capabilities to know if they can or cannot run transaction on given DDL. This commit demonstrates that this problem is present in DBAL too and it doesn't handle the case seamlessly. If consumer is not allowed to know if database allows transactional DDL, DBAL should handle attempts to execute DDL within transaction for databases not supporting transactional DDL seamlessly.
public function testTransactionalDDL(): void | ||
{ | ||
$this->connection->transactional(static function (Connection $conn): void { | ||
$table = new Table('foo', [new Column('bar', Type::getType('string'))]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is incomplete. It performs a single atomic statement that doesn't need to be wrapped in a transaction. Please modify by adding another always failing statement (e.g. throw new Exception()
after the foreach
). Then define the expected behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not testing failure here, but success. MySQL commits operation successfully and despite that, DBAL throws error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The successfull scenario doesn't need a transaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successful test scenario needs transaction if it exists to assert no exception has been thrown during successful transaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DDL statements are not transactional in MySQL and Oracle. No matter how much one wants them to be. If there are two DDLs in a transaction, and the second one fails, the first one will get committed no matter what API is used. I don't really understand this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's understandable that if there are two DDLs in a transaction, and the second one fails, the first one will get commited in MySQL. And in that case nobody would blame DBAL that it cannot do a rollback. But again, this test isn't about failure but success. If all operations in transaction()
block succeed, there is no reason for DBAL to throw exception.
You are saying this doesn't need to be running in transaction for MySQL... But library using DBAL doesn't know what kind of statements user passes in, it will just always use transactions, because there is no point not to. I also need to remind this is not a MySQL test. This test is being executed for all RDBMses. So it does make sense for it to be always running in transaction, because transactional DDL is supported in some of them. Similar rational applies for using this in libraries.
Now, I was going for minimal test scenario, but if you seriously need hardcoded test case where transaction looks more useful even on MySQL, I could just change the queries in transaction to do some insert
and then (successful) DDL. In such case when first (and only) DDL fails, changes would be rollbacked even in MySQL, so you would have to stop suggesting using transaction there doesn't make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But again, this test isn't about failure but success.
This is fundamentally wrong. There's no point in running a transaction if you don't care about error handling. The whole point of a transaction is error handling (well, and isolation).
If all operations in
transaction()
block succeed, there is no reason for DBAL to throw exception.
The method assumes that it controls the transaction flow but it fails to. Hence the exception.
I believe the approach taken in doctrine/migrations#1131 is right if you don't care about the changes being really transactional. I didn't know about |
The test failure on MySQL is expected because DBAL cannot guarantee the atomicity of the statements within the callback, and the exception clearly indicates that.
If it's not important whether the changes within the block are atomic or not (which is nonsence), |
Summary
Based on conversation at #4481 it's undesired that consumer should have capabilities to know if they can or cannot run transaction on given DDL. This commit demonstrates that this problem is present in DBAL too and it doesn't handle the case seamlessly. If consumer is not allowed to know if database allows transactional DDL, DBAL should handle attempts to execute DDL within transaction for databases not supporting transactional DDL seamlessly.