Hey Marco,
the bug is already reported as issue 5906
<https://github.com/doctrine/doctrine2/issues/5906>.
No the SQL server has not killed the connection. The exception on rollback
is totally correct, because the transaction has been terminated due to the
commit. The bug is that Doctrine does not handle the following condition
correctly: A transaction is committed, but the commitment fails. The
transaction is terminated and automatically rolled back by the SQL server.
Moreover, at client side the commit method throws an exception. This is
totally normal behavior because during a transaction most SQL constraints
are deferred until the commitment and checked simultaneously at the end and
not checked individually for each command. However, doctrine assumes that
the commit-method does not throw an exception and the commit-method is part
of the same try-block as the individual commands of the transaction. In the
(unexpected) case that the commitment throws an exception, the same
catch-block that handles the individual commands also handles this case and
tries to roll back the non-existing transaction.
Actually the following code in the inside of the DBAL code is wrong
$conn->beginTransaction();
try {
// Do some SQL stuff
$this->executeQuery( ... );
$conn->commit();
} catch (Exception $e) {
$this->em->close();
// BUG HERE: We only must roll back it the exception was not thrown by
$conn->commit();
$conn->rollback();
$this->afterTransactionRolledBack();
throw $e;
}
This bug was always there. It seems to be a difference between PHP
versions. In older PHP versions the plain PHP PDO commit function returned
TRUE or FALSE to indicate an error. Hence, a failing commit did not throw
an exception and did not erroneously triggered a superfluous rollback.
Newer version of PHP throw an exception and this causes the trouble.
However, not checking the return code of the old variant of commit was not
good neither, because this way an commitment error went silently unnoticed.
Am Donnerstag, 30. Juni 2016 13:59:25 UTC+2 schrieb Marco Pivetta:
>
> Hey Matthias,
>
> Are you able to find the component versions before the upgrade? Might be
> interesting to find out which component changed enough to cause the issue
> in first place...
>
> Also, it might be that your SQL server killed the connection for some
> reason, hence the exception on rollback...
>
> Marco Pivetta
>
> http://twitter.com/Ocramius
>
> http://ocramius.github.com/
>
> On 28 June 2016 at 20:07, Matthias Nagel <[email protected]
> <javascript:>> wrote:
>
>> I am using Doctrine together with Symfony (Doctrine 2.5.4, Docrine
>> Symfony Bundle 1.6.2 and Symfony 3.0.7). I have a project that I have not
>> touched for about a year. I now upgraded Symfony and Doctrine to the
>> aforementioned versions and now I suffer from some kind of "regression" bug.
>>
>> I rely on the implicit transaction handling provided by the Doctrine ORM
>> EntityManager. I know for sure that it worked before the upgrade but now I
>> get a PDO exception "There is no active transaction".
>>
>> Assume the case I have an entity that is supposed to be deleted from
>> database but there are still foreign keys that prohibited the particular
>> row from being deleted. Before the upgrade Doctrine
>>
>> 1. started a transaction,
>> 2. tried to delete the row from the database,
>> 3. committed the transaction
>> 4. did a rollback and then
>> 5. threw an exception that the row could not be deleted to due
>> foreign key constraints
>>
>> Now, after the upgrade Doctrine
>>
>> 1. starts a transaction,
>> 2. tries to delete the row from the database,
>> 3. commits the transaction
>> 4. tries a rollback and then
>> 5. throws an exception that there is no active transaction
>>
>> What?! I say "tries a rollback" in step 4 because the ROLLBACK command
>> never reaches my SQL server.
>>
>> This is my PHP code
>> /**
>> * @Route("/RentalUnit/{id}", name="hekdb_delete_rental_unit",
>> requirements={"id": "\d+"})
>> * @Method({"DELETE"})
>> */
>> public function deleteRentalUnit( Request $request, $id ) {
>> $id = intval( $id );
>> try {
>> $em = $this->getDoctrine()->getManager();
>> $rep = $em->getRepository( 'HEKdbBundle:RentalUnit' );
>> $unit = $rep->find( $id );
>> $em->remove( $unit );
>> $em->flush();
>> } catch( ORMException $e ) {
>> throw new InternalServerErrorHttpException( null, $e->getMessage
>> (), $e );
>> }
>> }
>>
>> Nothing, special about it. This code relies on implicit transaction
>> handling by $em->flush() and has worked before.
>>
>> The stacktrace is
>>
>> 1. at PDO->rollBack() in
>> vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php at line 1278
>> 2. at Connection->rollBack() in
>> vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php at line 413
>> 3. at UnitOfWork->commit(null) in
>> vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php at line 356
>> 4. at EntityManager->flush() in
>> src/HEK/HEKdbBundle/Controller/RentalUnitController.php at line 289
>> 5. at RentalUnitController->deleteRentalUnit( object(Request), '175' )
>>
>> In "vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php" I see
>> public function commit($entity = null) {
>> // ... lines left out
>>
>> // HERE a transaction is started
>> $conn->beginTransaction();
>>
>> try {
>> // ... lines left out
>> if ($this->entityDeletions) {
>> for ($count = count($commitOrder), $i = $count - 1; $i >= 0
>> && $this->entityDeletions; --$i) {
>> $this->executeDeletions($commitOrder[$i]);
>> }
>> }
>>
>> // Throws the expected execption due to violated foreign key
>> constraints
>> $conn->commit();
>> } catch (Exception $e) {
>> $this->em->close();
>> // The following line throws another exception inside the
>> exception
>> // handler. WHY?! :-(
>> $conn->rollback();
>> $this->afterTransactionRolledBack();
>> throw $e;
>> }
>> }
>>
>> Hence, a transaction should be started. According to the Symfony Debug
>> Toolbar the following SQL statements are
>> executed:
>>
>> 1. SELECT t0.id AS id_1, t0.rev AS rev_2, t0.display_name AS
>> display_name_3, t0.period AS period_4, t0.capacity AS capacity_5,
>> t0.comment AS comment_6 FROM rental_unit t0 WHERE t0.id = ?
>> Parameters: [175]
>> 2. "START TRANSACTION"
>> Parameters: { }
>> 3. DELETE FROM rental_unit WHERE id = ?
>> Parameters: [175]
>> 4. "COMMIT"
>> Parameters: { }
>> 5. "ROLLBACK"
>> Parameters: { }
>>
>> This looks totally fine and actually there is a transaction and even a
>> rollback. However, I think the rollback has never really happened, because
>> the exception occurred first. My server logs are
>>
>> 2016-06-28 19:21:39 CEST LOG: 00000: connection received: host=[local]
>> 2016-06-28 19:21:39 CEST LOCATION: BackendInitialize, postmaster.c:4134
>> 2016-06-28 19:21:39 CEST LOG: 00000: connection authorized: user=hekdbui
>> database=matthias.nagel_hekdb
>> 2016-06-28 19:21:39 CEST LOCATION: PerformAuthentication, postinit.c:258
>> 2016-06-28 19:21:39 CEST LOG: 00000: execute <unnamed>: SET NAMES 'UTF8'
>> 2016-06-28 19:21:39 CEST LOCATION: exec_execute_message, postgres.c:1932
>> 2016-06-28 19:21:39 CEST LOG: 00000: execute <unnamed>: SELECT t0.id AS
>> id_1, t0.rev AS rev_2, t0.display_name AS display_name_3, t0.period AS
>> period_4, t0.capacity AS capacity_5, t0.comment AS comment_6 FROM
>> rental_unit t0 WHERE t0.id = $1
>> 2016-06-28 19:21:39 CEST DETAIL: parameters: $1 = '175'
>> 2016-06-28 19:21:39 CEST LOCATION: exec_execute_message, postgres.c:1932
>> 2016-06-28 19:21:39 CEST LOG: 00000: statement: BEGIN
>> 2016-06-28 19:21:39 CEST LOCATION: exec_simple_query, postgres.c:914
>> 2016-06-28 19:21:39 CEST LOG: 00000: execute <unnamed>: DELETE FROM
>> rental_unit WHERE id = $1
>> 2016-06-28 19:21:39 CEST DETAIL: parameters: $1 = '175'
>> 2016-06-28 19:21:39 CEST LOCATION: exec_execute_message, postgres.c:1932
>> 2016-06-28 19:21:39 CEST LOG: 00000: statement: COMMIT
>> 2016-06-28 19:21:39 CEST LOCATION: exec_simple_query, postgres.c:914
>> 2016-06-28 19:21:39 CEST ERROR: 23503: update or delete on table
>> "rental_unit" violates foreign key constraint
>> "rental_unit_allocation_rental_unit_id_fkey" on table
>> "rental_unit_allocation"
>> 2016-06-28 19:21:39 CEST DETAIL: Key (id, period)=(175,
>> [2016-06-26,infinity)) is still referenced from table
>> "rental_unit_allocation".
>> 2016-06-28 19:21:39 CEST LOCATION: ri_ReportViolation, ri_triggers.c:3316
>> 2016-06-28 19:21:39 CEST STATEMENT: COMMIT
>> 2016-06-28 19:25:04 CEST LOG: 00000: checkpoint starting: time
>> 2016-06-28 19:25:04 CEST LOCATION: LogCheckpointStart, xlog.c:7957
>> 2016-06-28 19:25:05 CEST LOG: 00000: checkpoint complete: wrote 3
>> buffers (0.0%); 0 transaction log file(s) added, 0 removed, 0 recycled;
>> write=0.201 s, sync=0.001 s, total=0.206 s; sync files=3, longest=0.001 s,
>> average=0.000 s
>> 2016-06-28 19:25:05 CEST LOCATION: LogCheckpointEnd, xlog.c:8052
>>
>> This also looks very sane except that I do not see the "ROLLBACK" message.
>>
>> So, what is causing this error? Thank, you.
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "doctrine-user" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> To post to this group, send email to [email protected]
>> <javascript:>.
>> Visit this group at https://groups.google.com/group/doctrine-user.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
--
You received this message because you are subscribed to the Google Groups
"doctrine-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/doctrine-user.
For more options, visit https://groups.google.com/d/optout.