diff --git a/goatherd_test.go b/goatherd_test.go index c954559830e2e6db493035421e21d9e812b1c9ae..e342e74af9dcdc1a940c5115b58235449d577396 100644 --- a/goatherd_test.go +++ b/goatherd_test.go @@ -70,20 +70,20 @@ func create_user_t(t *testing.T) { t.Run("stdin", create_and_check("-", "-")) } -// runs two updating transactions concurrently, fails if none of them aborts -func transaction_conflict_t(t *testing.T) { +func interleaved_transactions_t(t *testing.T) { var err error var txs [2]*sql.Tx for i, _ := range txs { txs[i], err = db.Begin() t_err_fatal(t, err) - defer func(i int) { t.Log("rollback", i); txs[i].Rollback() }(i) + defer func(i int) { txs[i].Rollback() }(i) } var c uint64 - for _, tx := range txs { + for i, tx := range txs { err = tx.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c) if transaction_failed(err) { + t.Log("rollback after select", i) return } else { t_err_fatal(t, err) } } @@ -92,19 +92,71 @@ func transaction_conflict_t(t *testing.T) { t.Log("update", i) _, err := tx.Exec("UPDATE users SET count = $1 WHERE name = $2", c + uint64(i), username) if transaction_failed(err) { + t.Log("rollback after update", i) return } else { t_err_fatal(t, err) } t.Log("commit", i) err = tx.Commit() if transaction_failed(err) { + t.Log("rollback after commit", i) return } else { t_err_fatal(t, err) } } - t.Error("No transaction failure despite concurrent updates!") + t.Error("No transaction failure despite interleaved transactions!") +} + +func nested_transactions_t(t *testing.T) { + outer, err := db.Begin() + t_err_fatal(t, err) + defer outer.Rollback() + inner, err := db.Begin() + t_err_fatal(t, err) + defer inner.Rollback() + + var c uint64 + t_err_fatal(t, outer.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c)) + + err = inner.QueryRow("SELECT count FROM users WHERE name = $1", username).Scan(&c) + if transaction_failed(err) { + t.Log("rollback after inner.Query") + return + } else { t_err_fatal(t, err) } + + t.Log("update inner") + _, err = inner.Exec("UPDATE users SET count = $1 WHERE name = $2", c + 1, username) + if transaction_failed(err) { + t.Log("rollback after inner.Exec") + return + } else { t_err_fatal(t, err) } + + err = inner.Commit() + if transaction_failed(err) { + t.Log("rollback after inner.Commit") + return + } else { t_err_fatal(t, err) } + + t.Log("update outer") + _, err = outer.Exec("UPDATE users SET count = $1 WHERE name = $2", c + 1, username) + if transaction_failed(err) { + t.Log("rollback after outer.Exec") + return + } else { t_err_fatal(t, err) } + + err = outer.Commit() + if transaction_failed(err) { + t.Log("rollback after outer.Commit") + return + } else { t_err_fatal(t, err) } + + t.Error("No transaction failure despite nested transactions") } +func transaction_conflict_t(t *testing.T) { + t.Run("interleaved_transactions_t", interleaved_transactions_t) + t.Run("nested_transactions_t", nested_transactions_t) +} func check_offer_t(t *testing.T) { t.Run("no_such_user", func(t *testing.T) {