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
Fix #18406 - avoid PDOExceptions when working with autocommitted transactions #18407
Conversation
…committed transaction in PHP 8
Looks alright. Is it possible to cover it with a unit test? |
@samdark Sure, just added one. I put it in |
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.
Should be studied in more detail
@@ -161,15 +164,21 @@ public function commit() | |||
$this->_level--; | |||
if ($this->_level === 0) { | |||
Yii::debug('Commit transaction', __METHOD__); | |||
$this->db->pdo->commit(); | |||
// make sure the transaction wasn't autocommitted | |||
if ($this->db->pdo->inTransaction()) { |
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.
You have muted a bug of the function above IMHO.
if (!$this->getIsActive()) {
throw new Exception('Failed to commit transaction: transaction was inactive.');
}
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.
Right, I was too hasty with accepting this. Is there a significant difference now between $this->getIsActive()
and $this->db->pdo->inTransaction()
?
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.
@githubjeka As mentioned in #18406, I think it’s worth changing this behavior for Yii 3, but the point here is to avoid breaking backwards compatibility.
@bizley getIsActive()
is only concerned with the internal record of the transaction, but is unaware of whether the transaction is actually still active, which won’t be the case if you’re running MySQL and the transaction had been implicitly committed due to a schema change.
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'm wondering if this check could be placed in $this->getIsActive()
.
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.
Placing the check in $this->getIsActive()
is an interesting idea. May work well. @brandonkelly what do you think?
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.
Placing it there might be a good idea for Yii 3, but would be a BC break in the meantime, since commit()
and rollBack()
will each throw an exception if that returns false
. So all it would change (versus doing nothing) is that commit()
and rollBack()
would start throwing yii\db\Exception
s rather than PDOException
s.
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.
rollBack()
just returns nothing, only commit()
would throw exception. And while I'm going through the code for me it looks like getIsActive()
was never a good indicator whether the transaction is active (just checking a level and whether the PDO instance is set), it should always use PDO::inTransaction()
as well). IMO the current code is bad and what @brandonkelly proposes should be placed in getIsActive()
with upgrade info stating the users depending on previous code must update their apps, because simply ignoring autocommit was a bug. If the new check goes to getIsActive()
it fixes also setIsolationLevel()
because transaction must be active there as well.
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 completely agree that this PR isn’t great, but any other change would be significantly more involved, and would introduce a breaking change.
This is the situation:
- yii\db\Transaction has always (ignorantly) assumed that it is the authority on whether a transaction is currently active, and at what “level”.
- That has never been accurate with MySQL, due to implicit commits that Yii was unaware of
- A change to that behavior is going to lead to lots of code breakage
I’m simply saying that for now, ignorance is bliss, and Yii should just look the other way when it turns out the transaction isn’t actually active even though its record states that it is.
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.
Well, all you said is true. @samdark make a call please.
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 think, current solution for Yii 2, getIsActive()
for Yii 3 DB.
Co-authored-by: Bizley <pawel@positive.codes>
👍 |
This fixes #18406 by checking if there is an active transaction before committing/rolling back transactions.