2
0

unique_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. package storage
  2. import (
  3. "fmt"
  4. "strings"
  5. "sync"
  6. "testing"
  7. )
  8. func uniqueTable(t *testing.T, cols ...Column) (*SchemaManager, *TableManager) {
  9. t.Helper()
  10. _, _, schemas, tables := newTestSession(t)
  11. createTestTable(t, schemas, "t", cols)
  12. return schemas, tables
  13. }
  14. func TestInsertWithRowIDReturnsActualRowID(t *testing.T) {
  15. _, _, schemas, tables := newTestSession(t)
  16. createTestTable(t, schemas, "t", []Column{{Name: "id", Type: "INTEGER", PrimaryKey: true}})
  17. rid, err := tables.InsertWithRowID("t", Row{"id": int64(41)})
  18. if err != nil {
  19. t.Fatal(err)
  20. }
  21. if rid != 41 {
  22. t.Fatalf("explicit rowid = %d, want 41", rid)
  23. }
  24. // Auto-generated rowid is max+1, never a naive counter or MAX of a scan.
  25. rid, err = tables.InsertWithRowID("t", Row{})
  26. if err != nil {
  27. t.Fatal(err)
  28. }
  29. if rid != 42 {
  30. t.Fatalf("generated rowid = %d, want 42", rid)
  31. }
  32. }
  33. func TestInsertWithRowIDSkipsGaps(t *testing.T) {
  34. _, _, schemas, tables := newTestSession(t)
  35. createTestTable(t, schemas, "t", []Column{{Name: "id", Type: "INTEGER", PrimaryKey: true}})
  36. for _, id := range []int64{100, 5, 7} {
  37. if _, err := tables.InsertWithRowID("t", Row{"id": id}); err != nil {
  38. t.Fatal(err)
  39. }
  40. }
  41. rid, err := tables.InsertWithRowID("t", Row{})
  42. if err != nil {
  43. t.Fatal(err)
  44. }
  45. if rid != 101 {
  46. t.Fatalf("generated rowid = %d, want 101 (max+1, not a re-used gap)", rid)
  47. }
  48. }
  49. func TestUniqueIndexEnforcesOnInsert(t *testing.T) {
  50. schemas, tables := uniqueTable(t,
  51. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  52. Column{Name: "email", Type: "TEXT"},
  53. )
  54. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  55. t.Fatal(err)
  56. }
  57. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x"}); err != nil {
  58. t.Fatal(err)
  59. }
  60. err := tables.Insert("t", Row{"id": int64(2), "email": "a@x"})
  61. if err == nil {
  62. t.Fatal("expected unique violation on duplicate email")
  63. }
  64. if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
  65. t.Fatalf("unexpected error: %v", err)
  66. }
  67. // A distinct value still works.
  68. if err := tables.Insert("t", Row{"id": int64(2), "email": "b@x"}); err != nil {
  69. t.Fatalf("distinct email should succeed: %v", err)
  70. }
  71. }
  72. func TestUniqueIndexAllowsMultipleNulls(t *testing.T) {
  73. schemas, tables := uniqueTable(t,
  74. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  75. Column{Name: "email", Type: "TEXT", Nullable: true},
  76. )
  77. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  78. t.Fatal(err)
  79. }
  80. for i := int64(1); i <= 3; i++ {
  81. if err := tables.Insert("t", Row{"id": i, "email": nil}); err != nil {
  82. t.Fatalf("NULL insert %d should succeed: %v", i, err)
  83. }
  84. }
  85. if err := tables.Insert("t", Row{"id": int64(4), "email": "a@x"}); err != nil {
  86. t.Fatal(err)
  87. }
  88. if err := tables.Insert("t", Row{"id": int64(5), "email": "a@x"}); err == nil {
  89. t.Fatal("expected unique violation for non-NULL duplicate")
  90. }
  91. }
  92. func TestUniqueCompositeIndex(t *testing.T) {
  93. schemas, tables := uniqueTable(t,
  94. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  95. Column{Name: "a", Type: "TEXT"},
  96. Column{Name: "b", Type: "TEXT"},
  97. )
  98. if err := schemas.CreateIndex(&Index{Name: "uq_ab", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "a"}, {Name: "b"}}}); err != nil {
  99. t.Fatal(err)
  100. }
  101. if err := tables.Insert("t", Row{"id": int64(1), "a": "x", "b": "y"}); err != nil {
  102. t.Fatal(err)
  103. }
  104. if err := tables.Insert("t", Row{"id": int64(2), "a": "x", "b": "z"}); err != nil {
  105. t.Fatalf("distinct composite should succeed: %v", err)
  106. }
  107. if err := tables.Insert("t", Row{"id": int64(3), "a": "x", "b": "y"}); err == nil {
  108. t.Fatal("expected unique violation for duplicate composite (x,y)")
  109. }
  110. }
  111. func TestCreateUniqueIndexRejectsExistingDuplicates(t *testing.T) {
  112. schemas, tables := uniqueTable(t,
  113. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  114. Column{Name: "email", Type: "TEXT"},
  115. )
  116. for i := int64(1); i <= 2; i++ {
  117. if err := tables.Insert("t", Row{"id": i, "email": "dup@x"}); err != nil {
  118. t.Fatal(err)
  119. }
  120. }
  121. err := tables.CreateUniqueIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}})
  122. if err == nil {
  123. t.Fatal("expected CreateUniqueIndex to reject existing duplicates")
  124. }
  125. if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
  126. t.Fatalf("unexpected error: %v", err)
  127. }
  128. // The index must not be registered after the failed create.
  129. if schemas.IndexExists("uq_email") {
  130. t.Fatal("index should not exist after validation failure")
  131. }
  132. }
  133. func TestUniqueIndexEnforcesOnUpdate(t *testing.T) {
  134. schemas, tables := uniqueTable(t,
  135. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  136. Column{Name: "email", Type: "TEXT"},
  137. )
  138. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  139. t.Fatal(err)
  140. }
  141. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x"}); err != nil {
  142. t.Fatal(err)
  143. }
  144. if err := tables.Insert("t", Row{"id": int64(2), "email": "b@x"}); err != nil {
  145. t.Fatal(err)
  146. }
  147. _, _, err := tables.UpdateByPK("t", "2", func(Row) (Row, error) { return Row{"email": "a@x"}, nil })
  148. if err == nil {
  149. t.Fatal("expected unique violation when updating email to existing value")
  150. }
  151. if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
  152. t.Fatalf("unexpected error: %v", err)
  153. }
  154. }
  155. func TestUniqueIndexFreesOnDelete(t *testing.T) {
  156. schemas, tables := uniqueTable(t,
  157. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  158. Column{Name: "email", Type: "TEXT"},
  159. )
  160. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  161. t.Fatal(err)
  162. }
  163. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x"}); err != nil {
  164. t.Fatal(err)
  165. }
  166. if _, deleted, err := tables.DeleteByPK("t", "1"); err != nil || !deleted {
  167. t.Fatalf("delete: deleted=%v err=%v", deleted, err)
  168. }
  169. if err := tables.Insert("t", Row{"id": int64(2), "email": "a@x"}); err != nil {
  170. t.Fatalf("re-insert after delete should succeed: %v", err)
  171. }
  172. }
  173. func TestUniqueIndexTransactionCommitRejectsDuplicate(t *testing.T) {
  174. schemas, tables := uniqueTable(t,
  175. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  176. Column{Name: "email", Type: "TEXT"},
  177. )
  178. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  179. t.Fatal(err)
  180. }
  181. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x"}); err != nil {
  182. t.Fatal(err)
  183. }
  184. s := NewSession(schemas, tables)
  185. if err := s.Begin(); err != nil {
  186. t.Fatal(err)
  187. }
  188. if err := s.Insert("t", Row{"id": int64(2), "email": "a@x"}); err != nil {
  189. t.Fatalf("staged duplicate insert should not fail until commit: %v", err)
  190. }
  191. if err := s.Commit(); err == nil {
  192. t.Fatal("expected commit to reject unique violation")
  193. } else if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
  194. t.Fatalf("unexpected commit error: %v", err)
  195. }
  196. if _, err := tables.GetByPK("t", "2"); err != ErrKeyNotFound {
  197. t.Fatalf("conflicting row should not be durable: %v", err)
  198. }
  199. }
  200. func TestUniqueIndexTransactionSwap(t *testing.T) {
  201. schemas, tables := uniqueTable(t,
  202. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  203. Column{Name: "email", Type: "TEXT"},
  204. )
  205. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  206. t.Fatal(err)
  207. }
  208. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x"}); err != nil {
  209. t.Fatal(err)
  210. }
  211. if err := tables.Insert("t", Row{"id": int64(2), "email": "b@x"}); err != nil {
  212. t.Fatal(err)
  213. }
  214. s := NewSession(schemas, tables)
  215. if err := s.Begin(); err != nil {
  216. t.Fatal(err)
  217. }
  218. if _, _, err := s.UpdateByPK("t", "1", func(Row) (Row, error) { return Row{"email": "b@x"}, nil }); err != nil {
  219. t.Fatal(err)
  220. }
  221. if _, _, err := s.UpdateByPK("t", "2", func(Row) (Row, error) { return Row{"email": "a@x"}, nil }); err != nil {
  222. t.Fatal(err)
  223. }
  224. if err := s.Commit(); err != nil {
  225. t.Fatalf("swap of unique values should commit: %v", err)
  226. }
  227. }
  228. func TestUniqueIndexConcurrentInserts(t *testing.T) {
  229. schemas, tables := uniqueTable(t,
  230. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  231. Column{Name: "email", Type: "TEXT"},
  232. )
  233. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  234. t.Fatal(err)
  235. }
  236. const n = 32
  237. errs := make([]error, n)
  238. var wg sync.WaitGroup
  239. for i := 0; i < n; i++ {
  240. wg.Add(1)
  241. go func(i int) {
  242. defer wg.Done()
  243. errs[i] = tables.Insert("t", Row{"id": int64(i + 1), "email": "same@x"})
  244. }(i)
  245. }
  246. wg.Wait()
  247. ok := 0
  248. for _, e := range errs {
  249. if e == nil {
  250. ok++
  251. } else if !strings.Contains(e.Error(), "UNIQUE constraint failed") {
  252. t.Fatalf("unexpected insert error: %v", e)
  253. }
  254. }
  255. if ok != 1 {
  256. t.Fatalf("expected exactly one successful insert, got %d", ok)
  257. }
  258. if got := kvCount(t, tables, "t"); got != 1 {
  259. t.Fatalf("expected 1 row, got %d", got)
  260. }
  261. }
  262. func TestUniqueIndexConcurrentTransactions(t *testing.T) {
  263. schemas, tables := uniqueTable(t,
  264. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  265. Column{Name: "email", Type: "TEXT"},
  266. )
  267. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  268. t.Fatal(err)
  269. }
  270. s1 := NewSession(schemas, tables)
  271. s2 := NewSession(schemas, tables)
  272. if err := s1.Begin(); err != nil {
  273. t.Fatal(err)
  274. }
  275. if err := s2.Begin(); err != nil {
  276. t.Fatal(err)
  277. }
  278. if err := s1.Insert("t", Row{"id": int64(1), "email": "x@y"}); err != nil {
  279. t.Fatal(err)
  280. }
  281. if err := s2.Insert("t", Row{"id": int64(2), "email": "x@y"}); err != nil {
  282. t.Fatal(err)
  283. }
  284. errs := make([]error, 2)
  285. var wg sync.WaitGroup
  286. wg.Add(2)
  287. go func() { defer wg.Done(); errs[0] = s1.Commit() }()
  288. go func() { defer wg.Done(); errs[1] = s2.Commit() }()
  289. wg.Wait()
  290. ok, conflict := 0, 0
  291. for _, e := range errs {
  292. if e == nil {
  293. ok++
  294. } else if strings.Contains(e.Error(), "UNIQUE constraint failed") {
  295. conflict++
  296. } else {
  297. t.Fatalf("unexpected commit error: %v", e)
  298. }
  299. }
  300. if ok != 1 || conflict != 1 {
  301. t.Fatalf("expected one commit and one unique conflict, got ok=%d conflict=%d", ok, conflict)
  302. }
  303. if got := kvCount(t, tables, "t"); got != 1 {
  304. t.Fatalf("expected 1 durable row, got %d", got)
  305. }
  306. }
  307. func TestUniqueIndexUpdateNoChangeDoesNotSelfConflict(t *testing.T) {
  308. schemas, tables := uniqueTable(t,
  309. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  310. Column{Name: "email", Type: "TEXT"},
  311. Column{Name: "name", Type: "TEXT"},
  312. )
  313. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  314. t.Fatal(err)
  315. }
  316. if err := tables.Insert("t", Row{"id": int64(1), "email": "a@x", "name": "old"}); err != nil {
  317. t.Fatal(err)
  318. }
  319. // Update a non-indexed column; the unchanged unique value must not conflict.
  320. if _, updated, err := tables.UpdateByPK("t", "1", func(Row) (Row, error) { return Row{"name": "new"}, nil }); err != nil || !updated {
  321. t.Fatalf("no-op unique update: updated=%v err=%v", updated, err)
  322. }
  323. }
  324. func TestUniqueIndexValueEncodingDistinguishesTypes(t *testing.T) {
  325. schemas, tables := uniqueTable(t,
  326. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  327. Column{Name: "v", Type: "TEXT"},
  328. )
  329. if err := schemas.CreateIndex(&Index{Name: "uq_v", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "v"}}}); err != nil {
  330. t.Fatal(err)
  331. }
  332. if err := tables.Insert("t", Row{"id": int64(1), "v": int64(1)}); err != nil {
  333. t.Fatal(err)
  334. }
  335. // INTEGER 1 and TEXT "1" are distinct under a unique index.
  336. if err := tables.Insert("t", Row{"id": int64(2), "v": "1"}); err != nil {
  337. t.Fatalf("TEXT '1' should be distinct from INTEGER 1: %v", err)
  338. }
  339. if err := tables.Insert("t", Row{"id": int64(3), "v": int64(1)}); err == nil {
  340. t.Fatal("expected duplicate INTEGER 1 to be rejected")
  341. }
  342. }
  343. // TestConcurrentCreateUniqueIndexAndInsert races a duplicate insert against a
  344. // CREATE UNIQUE INDEX. The invariant is that the unique index can never end up
  345. // present while two rows share a value: either the index creation wins and the
  346. // insert fails the uniqueness scan, or the insert wins and the index creation
  347. // fails validating the existing duplicate.
  348. func TestConcurrentCreateUniqueIndexAndInsert(t *testing.T) {
  349. for iter := 0; iter < 200; iter++ {
  350. _, _, schemas, tables := newTestSession(t)
  351. createTestTable(t, schemas, "t", []Column{
  352. {Name: "id", Type: "INTEGER", PrimaryKey: true},
  353. {Name: "v", Type: "TEXT"},
  354. })
  355. if err := tables.Insert("t", Row{"id": int64(1), "v": "x"}); err != nil {
  356. t.Fatal(err)
  357. }
  358. var insertErr, indexErr error
  359. start := make(chan struct{})
  360. var wg sync.WaitGroup
  361. wg.Add(2)
  362. go func() {
  363. defer wg.Done()
  364. <-start
  365. insertErr = tables.Insert("t", Row{"id": int64(2), "v": "x"})
  366. }()
  367. go func() {
  368. defer wg.Done()
  369. <-start
  370. indexErr = tables.CreateUniqueIndex(&Index{Name: "uq_v", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "v"}}})
  371. }()
  372. close(start)
  373. wg.Wait()
  374. if schemas.IndexExists("uq_v") {
  375. rows, err := tables.Select("t", func(r Row) bool { return fmt.Sprintf("%v", r["v"]) == "x" })
  376. if err != nil {
  377. t.Fatal(err)
  378. }
  379. if len(rows) != 1 {
  380. t.Fatalf("iteration %d: unique index present but %d rows with v='x' (insertErr=%v indexErr=%v)", iter, len(rows), insertErr, indexErr)
  381. }
  382. }
  383. }
  384. }
  385. // TestConcurrentCreateUniqueIndexAndTransactionCommit exercises the same race
  386. // against a buffered transaction whose commit validates the final overlay.
  387. func TestConcurrentCreateUniqueIndexAndTransactionCommit(t *testing.T) {
  388. for iter := 0; iter < 100; iter++ {
  389. _, _, schemas, tables := newTestSession(t)
  390. createTestTable(t, schemas, "t", []Column{
  391. {Name: "id", Type: "INTEGER", PrimaryKey: true},
  392. {Name: "v", Type: "TEXT"},
  393. })
  394. if err := tables.Insert("t", Row{"id": int64(1), "v": "x"}); err != nil {
  395. t.Fatal(err)
  396. }
  397. s := NewSession(schemas, tables)
  398. if err := s.Begin(); err != nil {
  399. t.Fatal(err)
  400. }
  401. if err := s.Insert("t", Row{"id": int64(2), "v": "x"}); err != nil {
  402. t.Fatal(err)
  403. }
  404. var commitErr, indexErr error
  405. start := make(chan struct{})
  406. var wg sync.WaitGroup
  407. wg.Add(2)
  408. go func() {
  409. defer wg.Done()
  410. <-start
  411. commitErr = s.Commit()
  412. }()
  413. go func() {
  414. defer wg.Done()
  415. <-start
  416. indexErr = tables.CreateUniqueIndex(&Index{Name: "uq_v", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "v"}}})
  417. }()
  418. close(start)
  419. wg.Wait()
  420. if schemas.IndexExists("uq_v") {
  421. rows, err := tables.Select("t", func(r Row) bool { return fmt.Sprintf("%v", r["v"]) == "x" })
  422. if err != nil {
  423. t.Fatal(err)
  424. }
  425. if len(rows) != 1 {
  426. t.Fatalf("iteration %d: unique index present but %d rows with v='x' (commitErr=%v indexErr=%v)", iter, len(rows), commitErr, indexErr)
  427. }
  428. }
  429. }
  430. }
  431. func TestUniqueIndexIntegralNumericCanonicalization(t *testing.T) {
  432. schemas, tables := uniqueTable(t,
  433. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  434. Column{Name: "v", Type: "REAL"},
  435. )
  436. if err := schemas.CreateIndex(&Index{Name: "uq_v", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "v"}}}); err != nil {
  437. t.Fatal(err)
  438. }
  439. if err := tables.Insert("t", Row{"id": int64(1), "v": int64(1)}); err != nil {
  440. t.Fatal(err)
  441. }
  442. // A computed integral real (1.5-0.5) must collide with the integer 1.
  443. err := tables.Insert("t", Row{"id": int64(2), "v": 1.5 - 0.5})
  444. if err == nil {
  445. t.Fatal("expected float64(1.0) to collide with int64(1)")
  446. }
  447. if !strings.Contains(err.Error(), "UNIQUE constraint failed") {
  448. t.Fatalf("unexpected error: %v", err)
  449. }
  450. // A non-integral real remains distinct.
  451. if err := tables.Insert("t", Row{"id": int64(3), "v": 1.5}); err != nil {
  452. t.Fatalf("distinct non-integral real should succeed: %v", err)
  453. }
  454. }
  455. func TestUniqueIndexUnsignedCanonicalization(t *testing.T) {
  456. schemas, tables := uniqueTable(t,
  457. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  458. Column{Name: "v", Type: "INTEGER"},
  459. )
  460. if err := schemas.CreateIndex(&Index{Name: "uq_v", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "v"}}}); err != nil {
  461. t.Fatal(err)
  462. }
  463. if err := tables.Insert("t", Row{"id": int64(1), "v": uint64(7)}); err != nil {
  464. t.Fatal(err)
  465. }
  466. // unsigned 7 and signed 7 are the same integer value.
  467. if err := tables.Insert("t", Row{"id": int64(2), "v": int64(7)}); err == nil {
  468. t.Fatal("expected uint64(7) to collide with int64(7)")
  469. }
  470. }
  471. func TestUniqueIndexDeleteReinsertTextPK(t *testing.T) {
  472. schemas, tables := uniqueTable(t,
  473. Column{Name: "id", Type: "TEXT", PrimaryKey: true},
  474. Column{Name: "email", Type: "TEXT"},
  475. )
  476. if err := schemas.CreateIndex(&Index{Name: "uq_email", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "email"}}}); err != nil {
  477. t.Fatal(err)
  478. }
  479. if err := tables.Insert("t", Row{"id": "alice", "email": "a@x"}); err != nil {
  480. t.Fatal(err)
  481. }
  482. // Deleting and re-inserting the same TEXT primary key with the same unique
  483. // value must not self-conflict: the re-insert is a new rowid but replaces the
  484. // same durable data key.
  485. s := NewSession(schemas, tables)
  486. if err := s.Begin(); err != nil {
  487. t.Fatal(err)
  488. }
  489. if _, deleted, err := s.DeleteByPK("t", "alice"); err != nil || !deleted {
  490. t.Fatalf("delete: deleted=%v err=%v", deleted, err)
  491. }
  492. if err := s.Insert("t", Row{"id": "alice", "email": "a@x"}); err != nil {
  493. t.Fatal(err)
  494. }
  495. if err := s.Commit(); err != nil {
  496. t.Fatalf("delete+reinsert same PK should commit, got: %v", err)
  497. }
  498. rows, err := tables.Select("t", nil)
  499. if err != nil || len(rows) != 1 || rows[0]["email"] != "a@x" {
  500. t.Fatalf("expected exactly one row with email a@x, got %v (err=%v)", rows, err)
  501. }
  502. }
  503. func TestUniqueIndexesSameValueDifferentIndexes(t *testing.T) {
  504. schemas, tables := uniqueTable(t,
  505. Column{Name: "id", Type: "INTEGER", PrimaryKey: true},
  506. Column{Name: "name", Type: "TEXT"},
  507. Column{Name: "lower_name", Type: "TEXT"},
  508. )
  509. if err := schemas.CreateIndex(&Index{Name: "UQE_user_name", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "name"}}}); err != nil {
  510. t.Fatal(err)
  511. }
  512. if err := schemas.CreateIndex(&Index{Name: "UQE_user_lower_name", Table: "t", Unique: true, Columns: []IndexColumn{{Name: "lower_name"}}}); err != nil {
  513. t.Fatal(err)
  514. }
  515. // A single row carrying the same value in two different unique indexes must
  516. // not self-collide (the seen set is per-index, not per-value).
  517. if err := tables.Insert("t", Row{"id": int64(1), "name": "alice", "lower_name": "alice"}); err != nil {
  518. t.Fatalf("same value across two unique indexes should not self-collide: %v", err)
  519. }
  520. // A second row with the same value in ONE index must still collide.
  521. if err := tables.Insert("t", Row{"id": int64(2), "name": "bob", "lower_name": "alice"}); err == nil {
  522. t.Fatal("expected unique violation on lower_name")
  523. }
  524. if err := tables.Insert("t", Row{"id": int64(2), "name": "alice", "lower_name": "bob"}); err == nil {
  525. t.Fatal("expected unique violation on name")
  526. }
  527. if err := tables.Insert("t", Row{"id": int64(2), "name": "bob", "lower_name": "bob"}); err != nil {
  528. t.Fatalf("distinct values should insert: %v", err)
  529. }
  530. }