2
0

alter_table_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. package executor
  2. import (
  3. "sync"
  4. "testing"
  5. )
  6. // ADD COLUMN only appends schema metadata. Existing rows are not rewritten, so
  7. // a declared DEFAULT applies to later INSERTs only.
  8. func TestAlterTableAddColumnDoesNotBackfillPopulatedTable(t *testing.T) {
  9. _, schema, table := newTestDB(t)
  10. e := newExec(schema, table)
  11. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY)")
  12. execMust(t, e, "INSERT INTO t (id) VALUES (1), (2)")
  13. execMust(t, e, "ALTER TABLE t ADD COLUMN note TEXT DEFAULT 'later'")
  14. res := execMust(t, e, "SELECT id, note FROM t ORDER BY id")
  15. if res.RowCount != 2 {
  16. t.Fatalf("row count = %d, want 2", res.RowCount)
  17. }
  18. for _, row := range res.Rows {
  19. if row[1] != nil {
  20. t.Fatalf("existing row %v note = %v, want NULL (no backfill)", row[0], row[1])
  21. }
  22. }
  23. execMust(t, e, "INSERT INTO t (id) VALUES (3)")
  24. res = execMust(t, e, "SELECT note FROM t WHERE id = 3")
  25. if got := res.Rows[0][0]; got != "later" {
  26. t.Fatalf("new row note = %v, want later", got)
  27. }
  28. }
  29. func TestAlterTableAddColumnIfNotExistsIsIdempotent(t *testing.T) {
  30. _, schema, table := newTestDB(t)
  31. e := newExec(schema, table)
  32. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY)")
  33. execMust(t, e, "ALTER TABLE t ADD COLUMN IF NOT EXISTS c TEXT")
  34. execMust(t, e, "ALTER TABLE t ADD COLUMN IF NOT EXISTS c TEXT")
  35. s, err := schema.GetSchema("t")
  36. if err != nil {
  37. t.Fatalf("get schema: %v", err)
  38. }
  39. count := 0
  40. for _, col := range s.Columns {
  41. if col.Name == "c" {
  42. count++
  43. }
  44. }
  45. if count != 1 {
  46. t.Fatalf("column c appears %d times, want 1", count)
  47. }
  48. }
  49. // Concurrent IF NOT EXISTS ADD COLUMN on one engine instance must not duplicate
  50. // metadata; the schema manager serializes the check-and-append.
  51. func TestAlterTableAddColumnIfNotExistsConcurrent(t *testing.T) {
  52. _, schema, table := newTestDB(t)
  53. e := newExec(schema, table)
  54. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY)")
  55. var wg sync.WaitGroup
  56. for i := 0; i < 8; i++ {
  57. wg.Add(1)
  58. go func() {
  59. defer wg.Done()
  60. session := newExec(schema, table)
  61. if _, err := execSQL(session, "ALTER TABLE t ADD COLUMN IF NOT EXISTS c TEXT"); err != nil {
  62. t.Errorf("add column: %v", err)
  63. }
  64. }()
  65. }
  66. wg.Wait()
  67. s, err := schema.GetSchema("t")
  68. if err != nil {
  69. t.Fatalf("get schema: %v", err)
  70. }
  71. count := 0
  72. for _, col := range s.Columns {
  73. if col.Name == "c" {
  74. count++
  75. }
  76. }
  77. if count != 1 {
  78. t.Fatalf("column c appears %d times, want 1", count)
  79. }
  80. }
  81. // DROP COLUMN rewrites every remaining row so the removed value does not stay
  82. // in durable storage.
  83. func TestAlterTableDropColumnRewritesPopulatedRows(t *testing.T) {
  84. _, schema, table := newTestDB(t)
  85. e := newExec(schema, table)
  86. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, keep TEXT, dropme TEXT)")
  87. execMust(t, e, "INSERT INTO t (id, keep, dropme) VALUES (1, 'a', 'secret'), (2, 'b', 'secret')")
  88. execMust(t, e, "ALTER TABLE t DROP COLUMN dropme")
  89. res := execMust(t, e, "SELECT * FROM t ORDER BY id")
  90. if len(res.Columns) != 2 || res.Columns[0] != "id" || res.Columns[1] != "keep" {
  91. t.Fatalf("columns after drop = %v, want [id keep]", res.Columns)
  92. }
  93. if res.RowCount != 2 || res.Rows[0][1] != "a" || res.Rows[1][1] != "b" {
  94. t.Fatalf("rows after drop = %v", res.Rows)
  95. }
  96. if _, err := execSQL(e, "SELECT dropme FROM t"); err == nil {
  97. t.Fatal("expected dropped column to be unknown")
  98. }
  99. }
  100. // RENAME COLUMN rewrites row keys but deliberately leaves index metadata
  101. // unchanged. Documented guidance: drop and recreate indexes after a rename.
  102. func TestAlterTableRenameColumnLeavesIndexMetadataStale(t *testing.T) {
  103. _, schema, table := newTestDB(t)
  104. e := newExec(schema, table)
  105. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, a TEXT)")
  106. execMust(t, e, "INSERT INTO t (id, a) VALUES (1, 'x')")
  107. execMust(t, e, "CREATE INDEX idx_a ON t(a)")
  108. execMust(t, e, "ALTER TABLE t RENAME COLUMN a TO aa")
  109. res := execMust(t, e, "SELECT aa FROM t WHERE id = 1")
  110. if got := res.Rows[0][0]; got != "x" {
  111. t.Fatalf("renamed value = %v, want x", got)
  112. }
  113. names, err := schema.ListIndexes()
  114. if err != nil {
  115. t.Fatalf("list indexes: %v", err)
  116. }
  117. found := false
  118. for _, name := range names {
  119. if name == "idx_a" {
  120. found = true
  121. }
  122. }
  123. if !found {
  124. t.Fatalf("indexes = %v, want idx_a to still exist (rename does not rewrite index metadata)", names)
  125. }
  126. }