2
0

update_subquery_deadlock_test.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package executor
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. // runWithTimeout runs fn and fails if it does not finish, which detects the
  7. // organization-delete deadlock (UPDATE ... WHERE id IN (SELECT ...)).
  8. func runWithTimeout(t *testing.T, d time.Duration, fn func()) {
  9. t.Helper()
  10. done := make(chan struct{})
  11. go func() {
  12. defer close(done)
  13. fn()
  14. }()
  15. select {
  16. case <-done:
  17. case <-time.After(d):
  18. t.Fatalf("operation did not finish within %s (deadlock)", d)
  19. }
  20. }
  21. func TestUpdateWhereInSubqueryInTransaction(t *testing.T) {
  22. _, schema, table := newTestDB(t)
  23. e := newExec(schema, table)
  24. execMust(t, e, "CREATE TABLE repository (id INTEGER PRIMARY KEY, num_watches INTEGER)")
  25. execMust(t, e, "CREATE TABLE watch (id INTEGER PRIMARY KEY, user_id INTEGER, repo_id INTEGER)")
  26. execMust(t, e, "INSERT INTO repository VALUES (1, 5)")
  27. execMust(t, e, "INSERT INTO repository VALUES (2, 7)")
  28. execMust(t, e, "INSERT INTO watch VALUES (1, 1, 1)")
  29. execMust(t, e, "BEGIN")
  30. runWithTimeout(t, 5*time.Second, func() {
  31. execMust(t, e, "UPDATE repository SET num_watches = num_watches - 1 WHERE id IN (SELECT repo_id FROM watch WHERE user_id = 1)")
  32. })
  33. execMust(t, e, "COMMIT")
  34. res := execMust(t, e, "SELECT num_watches FROM repository WHERE id = 1")
  35. if res.Rows[0][0] != int64(4) {
  36. t.Fatalf("num_watches = %v, want 4", res.Rows[0][0])
  37. }
  38. }
  39. func TestDeleteWhereInSubqueryInTransaction(t *testing.T) {
  40. _, schema, table := newTestDB(t)
  41. e := newExec(schema, table)
  42. execMust(t, e, "CREATE TABLE repository (id INTEGER PRIMARY KEY, num_watches INTEGER)")
  43. execMust(t, e, "CREATE TABLE watch (id INTEGER PRIMARY KEY, user_id INTEGER, repo_id INTEGER)")
  44. execMust(t, e, "INSERT INTO repository VALUES (1, 5)")
  45. execMust(t, e, "INSERT INTO repository VALUES (2, 7)")
  46. execMust(t, e, "INSERT INTO watch VALUES (1, 1, 1)")
  47. execMust(t, e, "BEGIN")
  48. runWithTimeout(t, 5*time.Second, func() {
  49. execMust(t, e, "DELETE FROM repository WHERE id IN (SELECT repo_id FROM watch WHERE user_id = 1)")
  50. })
  51. execMust(t, e, "COMMIT")
  52. res := execMust(t, e, "SELECT id FROM repository")
  53. if res.RowCount != 1 || res.Rows[0][0] != int64(2) {
  54. t.Fatalf("remaining rows = %v, want [2]", res.Rows)
  55. }
  56. }