rails/rails — pull request #10939 by tovodeverett (2013-06-13 20:20:57+00:00)
Title: Fix transaction bug in PostgreSQLAdapter#disable_referential_integrity

PostgreSQL requires a `SAVEPOINT` and `ROLLBACK` to resume execution after a failed statement within a transaction. Since normal testing is done with superuser privs, the `rescue` clauses in `PostgreSQLAdapter#disable_referential_integrity` weren't being tested in the context of a transaction.

The `test/cases/adapters/postgresql/referential_integrity_test.rb` file was based on merging one of the PostgreSQL-specific test files with `test_disable_referential_integrity` from `test/cases/adapter_test.rb`. The test `test_disable_referential_integrity_as_nonsuperuser_is_harmless_in_transaction` creates a temporary role to hold superuser privs and then relinquishes superuser privs for the current user before carrying out the actual test and then uses that temporary role to regain superuser privs.

The code added to `PostgreSQLAdapter#disable_referential_integrity` creates `SAVEPOINT`s before attempting statements in the blocks with `rescue` clauses and then uses `ROLLBACK` in the `rescue` clauses. Note that creating a `SAVEPOINT` isn't permitted if we're not in a transaction, thus the statement level `rescue` to handle that case and to assign a useful value to `in_transaction`. The `SAVEPOINT`s are released when they are no longer needed to minimize any impact on resources.

Note that it appears that the tests in `referential_integrity_test.rb` are being run in some sort of implicit transaction, but that there are a number of tests in the rest of the test suite that appear to run outside of transactions and that failed when the code didn't deal with
the non-transaction scenario. I'm not sure how to test for the non-transaction scenario explicitly in `referential_integrity_test.rb`, but there appears to be comprehensive coverage from the rest of the suite. I left the explicit call to `transaction` in my test to ensure that if whatever creates the implicit transaction changes the test is still valid.