unique_test.go 20 KB

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