public void Issue604_RecoversFromFailedCommit() { db.Trace = true; var initialCount = db.Table <TestObj> ().Count(); // // Well this is an issue because there is an internal variable called _transactionDepth // that tries to track if we are in an active transaction. // The problem is, _transactionDepth is set to 0 and then commit is executed on the database. // Well, the commit fails and "When COMMIT fails in this way, the transaction remains active and // the COMMIT can be retried later after the reader has had a chance to clear" // var rollbacks = 0; db.Tracer = m => { if (m == "Executing: commit") { throw SQLiteException.New(SQLite3.Result.Busy, "Make commit fail"); } if (m == "Executing: rollback") { rollbacks++; } }; db.BeginTransaction(); db.Insert(new TestObj()); try { db.Commit(); Assert.Fail("Should have thrown"); } catch (SQLiteException ex) when(ex.Result == SQLite3.Result.Busy) { db.Tracer = null; } Assert.False(db.IsInTransaction); Assert.AreEqual(1, rollbacks); // // The catch statements in the RunInTransaction family of functions catch this and call rollback, // but since _transactionDepth is 0, the transaction isn't actually rolled back. // // So the next time begin transaction is called on the same connection, // sqlite-net attempts to begin a new transaction (because _transactionDepth is 0), // which promptly fails because there is still an active transaction on the connection. // // Well now we are in big trouble because _transactionDepth got set to 1, // and when begin transaction fails in this manner, the transaction isn't rolled back // (which would have set _transactionDepth to 0) // db.BeginTransaction(); db.Insert(new TestObj()); db.Commit(); Assert.AreEqual(initialCount + 1, db.Table <TestObj> ().Count()); }