0.9.1 - #transaction may wrongly end enclosing transaction #19

Open
wants to merge 1 commit into
from

Conversation

Projects
None yet
2 participants
Owner

pilcrow commented Jan 10, 2011

Issue

rdbi-0.9.1

RDBI::Database#transaction may incorrectly end the enclosing transaction at the conclusion of a nested transaction block, if the user has already explicitly COMMITted or ROLLBACKed the inner transaction.

Presently, #transaction, checks only to see that the dbh is within any transaction, so it cannot tell whether a block concludes in the same (possibly nested) transaction which it began.

The impact is likely limited, as nested transaction support (SAVEPOINTs?) is not to my knowledge implemented in any supported backend.

Example

Presuming a database with nested transaction support:
# initial tx depth -> 0

dbh.transaction do |dbh|    # implicit BEGIN
                            #   tx++ -> 1

  dbh.execute(SQL_1)

  dbh.transaction do |dbh|  # implicit BEGIN
                            #   tx++ -> 2
    dbh.execute(SQL_2)

    dbh.rollback            # explicit ROLLBACK:  SQL_2 undone
                            #   tx-- -> 1
  end                       # implicit COMMIT:  SQL_1 committed
                            #   ERROR - SQL_1 committed too early
                            #   tx-- -> 0

  dbh.execute(SQL_3)

  #dbh.commit               # ERROR - explicit COMMIT would be ignored here
end                         # ERROR - implicit COMMIT ignored, too
                            # ERROR - SQL_3 uncommitted

Proposed Fix

This pull request simply has #transaction remember its tx depth upon entry, and only #commit or #rollback if the depth is as expected after leaving the user block.

Tests

I have no tests for this fix.

@pilcrow pilcrow Make #transaction check nested tx depth before implicit COMMIT/ROLLBACK.
Previously, #transaction only checked to see that it was inside *any* tx
before ending (COMMIT/ROLLBACK) the open tx.  However, the ability of users
to explicitly end a tx inside the block means that the #transaction block
might end in an enclosing tx wherein the implicit COMMIT/ROLLBACK behavior
intended for the *nested* transaction is incorrect.

Example:

    dbh.transaction do |dbh|   # tx depth 1
      dbh.execute(STMT_1)
      dbh.transaction do |dbh| # tx depth 2
        dbh.execute(STMT_2)
        dbh.commit             # tx depth 1 (#rollback would do the same)
      end                      # tx depth 0 (implicit COMMIT -- to early)
      dbh.execute(STMT_3)
    end                        # implicit COMMIT of STMT_3 ignored!
                               # explicit would have been ignored, too
a1ef450

erikh commented Jan 16, 2011

Ok; I'm gonna need tests for this one, for sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment