alter_table_test.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. if _, err := execSQL(e, "ALTER TABLE t ADD COLUMN IF NOT EXISTS c TEXT"); err != nil {
  61. t.Errorf("add column: %v", err)
  62. }
  63. }()
  64. }
  65. wg.Wait()
  66. s, err := schema.GetSchema("t")
  67. if err != nil {
  68. t.Fatalf("get schema: %v", err)
  69. }
  70. count := 0
  71. for _, col := range s.Columns {
  72. if col.Name == "c" {
  73. count++
  74. }
  75. }
  76. if count != 1 {
  77. t.Fatalf("column c appears %d times, want 1", count)
  78. }
  79. }
  80. // DROP COLUMN rewrites every remaining row so the removed value does not stay
  81. // in durable storage.
  82. func TestAlterTableDropColumnRewritesPopulatedRows(t *testing.T) {
  83. _, schema, table := newTestDB(t)
  84. e := newExec(schema, table)
  85. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, keep TEXT, dropme TEXT)")
  86. execMust(t, e, "INSERT INTO t (id, keep, dropme) VALUES (1, 'a', 'secret'), (2, 'b', 'secret')")
  87. execMust(t, e, "ALTER TABLE t DROP COLUMN dropme")
  88. res := execMust(t, e, "SELECT * FROM t ORDER BY id")
  89. if len(res.Columns) != 2 || res.Columns[0] != "id" || res.Columns[1] != "keep" {
  90. t.Fatalf("columns after drop = %v, want [id keep]", res.Columns)
  91. }
  92. if res.RowCount != 2 || res.Rows[0][1] != "a" || res.Rows[1][1] != "b" {
  93. t.Fatalf("rows after drop = %v", res.Rows)
  94. }
  95. if _, err := execSQL(e, "SELECT dropme FROM t"); err == nil {
  96. t.Fatal("expected dropped column to be unknown")
  97. }
  98. }
  99. // RENAME COLUMN rewrites row keys but deliberately leaves index metadata
  100. // unchanged. Documented guidance: drop and recreate indexes after a rename.
  101. func TestAlterTableRenameColumnLeavesIndexMetadataStale(t *testing.T) {
  102. _, schema, table := newTestDB(t)
  103. e := newExec(schema, table)
  104. execMust(t, e, "CREATE TABLE t (id INTEGER PRIMARY KEY, a TEXT)")
  105. execMust(t, e, "INSERT INTO t (id, a) VALUES (1, 'x')")
  106. execMust(t, e, "CREATE INDEX idx_a ON t(a)")
  107. execMust(t, e, "ALTER TABLE t RENAME COLUMN a TO aa")
  108. res := execMust(t, e, "SELECT aa FROM t WHERE id = 1")
  109. if got := res.Rows[0][0]; got != "x" {
  110. t.Fatalf("renamed value = %v, want x", got)
  111. }
  112. names, err := schema.ListIndexes()
  113. if err != nil {
  114. t.Fatalf("list indexes: %v", err)
  115. }
  116. found := false
  117. for _, name := range names {
  118. if name == "idx_a" {
  119. found = true
  120. }
  121. }
  122. if !found {
  123. t.Fatalf("indexes = %v, want idx_a to still exist (rename does not rewrite index metadata)", names)
  124. }
  125. }