migration.exs (17967B)
1 defmodule Ecto.Integration.MigrationTest do 2 use ExUnit.Case, async: true 3 4 alias Ecto.Integration.{TestRepo, PoolRepo} 5 6 defmodule CreateMigration do 7 use Ecto.Migration 8 9 @table table(:create_table_migration) 10 @index index(:create_table_migration, [:value], unique: true) 11 12 def up do 13 create @table do 14 add :value, :integer 15 end 16 create @index 17 end 18 19 def down do 20 drop @index 21 drop @table 22 end 23 end 24 25 defmodule AddColumnMigration do 26 use Ecto.Migration 27 28 def up do 29 create table(:add_col_migration) do 30 add :value, :integer 31 end 32 33 alter table(:add_col_migration) do 34 add :to_be_added, :integer 35 end 36 37 execute "INSERT INTO add_col_migration (value, to_be_added) VALUES (1, 2)" 38 end 39 40 def down do 41 drop table(:add_col_migration) 42 end 43 end 44 45 defmodule AlterColumnMigration do 46 use Ecto.Migration 47 48 def up do 49 create table(:alter_col_migration) do 50 add :from_null_to_not_null, :integer 51 add :from_not_null_to_null, :integer, null: false 52 53 add :from_default_to_no_default, :integer, default: 0 54 add :from_no_default_to_default, :integer 55 end 56 57 alter table(:alter_col_migration) do 58 modify :from_null_to_not_null, :string, null: false 59 modify :from_not_null_to_null, :string, null: true 60 61 modify :from_default_to_no_default, :integer, default: nil 62 modify :from_no_default_to_default, :integer, default: 0 63 end 64 65 execute "INSERT INTO alter_col_migration (from_null_to_not_null) VALUES ('foo')" 66 end 67 68 def down do 69 drop table(:alter_col_migration) 70 end 71 end 72 73 defmodule AlterColumnFromMigration do 74 use Ecto.Migration 75 76 def change do 77 create table(:modify_from_products) do 78 add :value, :integer 79 add :nullable, :integer, null: false 80 end 81 82 if direction() == :up do 83 flush() 84 PoolRepo.insert_all "modify_from_products", [[value: 1, nullable: 1]] 85 end 86 87 alter table(:modify_from_products) do 88 modify :value, :bigint, from: :integer 89 modify :nullable, :bigint, null: true, from: {:integer, null: false} 90 end 91 end 92 end 93 94 defmodule AlterColumnFromPkeyMigration do 95 use Ecto.Migration 96 97 def change do 98 create table(:modify_from_authors, primary_key: false) do 99 add :id, :integer, primary_key: true 100 end 101 create table(:modify_from_posts) do 102 add :author_id, references(:modify_from_authors, type: :integer) 103 end 104 105 if direction() == :up do 106 flush() 107 PoolRepo.insert_all "modify_from_authors", [[id: 1]] 108 PoolRepo.insert_all "modify_from_posts", [[author_id: 1]] 109 end 110 111 alter table(:modify_from_posts) do 112 # remove the constraints modify_from_posts_author_id_fkey 113 modify :author_id, :integer, from: references(:modify_from_authors, type: :integer) 114 end 115 alter table(:modify_from_authors) do 116 modify :id, :bigint, from: :integer 117 end 118 alter table(:modify_from_posts) do 119 # add the constraints modify_from_posts_author_id_fkey 120 modify :author_id, references(:modify_from_authors, type: :bigint), from: :integer 121 end 122 end 123 end 124 125 defmodule AlterForeignKeyOnDeleteMigration do 126 use Ecto.Migration 127 128 def up do 129 create table(:alter_fk_users) 130 131 create table(:alter_fk_posts) do 132 add :alter_fk_user_id, :id 133 end 134 135 alter table(:alter_fk_posts) do 136 modify :alter_fk_user_id, references(:alter_fk_users, on_delete: :nilify_all) 137 end 138 end 139 140 def down do 141 drop table(:alter_fk_posts) 142 drop table(:alter_fk_users) 143 end 144 end 145 146 defmodule AlterForeignKeyOnUpdateMigration do 147 use Ecto.Migration 148 149 def up do 150 create table(:alter_fk_users) 151 152 create table(:alter_fk_posts) do 153 add :alter_fk_user_id, :id 154 end 155 156 alter table(:alter_fk_posts) do 157 modify :alter_fk_user_id, references(:alter_fk_users, on_update: :update_all) 158 end 159 end 160 161 def down do 162 drop table(:alter_fk_posts) 163 drop table(:alter_fk_users) 164 end 165 end 166 167 defmodule DropColumnMigration do 168 use Ecto.Migration 169 170 def up do 171 create table(:drop_col_migration) do 172 add :value, :integer 173 add :to_be_removed, :integer 174 end 175 176 execute "INSERT INTO drop_col_migration (value, to_be_removed) VALUES (1, 2)" 177 178 alter table(:drop_col_migration) do 179 remove :to_be_removed 180 end 181 end 182 183 def down do 184 drop table(:drop_col_migration) 185 end 186 end 187 188 defmodule RenameColumnMigration do 189 use Ecto.Migration 190 191 def up do 192 create table(:rename_col_migration) do 193 add :to_be_renamed, :integer 194 end 195 196 rename table(:rename_col_migration), :to_be_renamed, to: :was_renamed 197 198 execute "INSERT INTO rename_col_migration (was_renamed) VALUES (1)" 199 end 200 201 def down do 202 drop table(:rename_col_migration) 203 end 204 end 205 206 defmodule OnDeleteMigration do 207 use Ecto.Migration 208 209 def up do 210 create table(:parent1) 211 create table(:parent2) 212 213 create table(:ref_migration) do 214 add :parent1, references(:parent1, on_delete: :nilify_all) 215 end 216 217 alter table(:ref_migration) do 218 add :parent2, references(:parent2, on_delete: :delete_all) 219 end 220 end 221 222 def down do 223 drop table(:ref_migration) 224 drop table(:parent1) 225 drop table(:parent2) 226 end 227 end 228 229 defmodule CompositeForeignKeyMigration do 230 use Ecto.Migration 231 232 def change do 233 create table(:composite_parent) do 234 add :key_id, :integer 235 end 236 237 create unique_index(:composite_parent, [:id, :key_id]) 238 239 create table(:composite_child) do 240 add :parent_key_id, :integer 241 add :parent_id, references(:composite_parent, with: [parent_key_id: :key_id]) 242 end 243 end 244 end 245 246 defmodule ReferencesRollbackMigration do 247 use Ecto.Migration 248 249 def change do 250 create table(:parent) do 251 add :name, :string 252 end 253 254 create table(:child) do 255 add :parent_id, references(:parent) 256 end 257 end 258 end 259 260 defmodule RenameMigration do 261 use Ecto.Migration 262 263 @table_current table(:posts_migration) 264 @table_new table(:new_posts_migration) 265 266 def up do 267 create @table_current 268 rename @table_current, to: @table_new 269 end 270 271 def down do 272 drop @table_new 273 end 274 end 275 276 defmodule PrefixMigration do 277 use Ecto.Migration 278 279 @prefix "ecto_prefix_test" 280 281 def up do 282 execute TestRepo.create_prefix(@prefix) 283 create table(:first, prefix: @prefix) 284 create table(:second, prefix: @prefix) do 285 add :first_id, references(:first) 286 end 287 end 288 289 def down do 290 drop table(:second, prefix: @prefix) 291 drop table(:first, prefix: @prefix) 292 execute TestRepo.drop_prefix(@prefix) 293 end 294 end 295 296 defmodule NoSQLMigration do 297 use Ecto.Migration 298 299 def up do 300 create table(:collection, options: [capped: true]) 301 execute create: "collection" 302 end 303 end 304 305 defmodule Parent do 306 use Ecto.Schema 307 308 schema "parent" do 309 end 310 end 311 312 defmodule NoErrorTableMigration do 313 use Ecto.Migration 314 315 def change do 316 create_if_not_exists table(:existing) do 317 add :name, :string 318 end 319 320 create_if_not_exists table(:existing) do 321 add :name, :string 322 end 323 324 create_if_not_exists table(:existing) 325 326 drop_if_exists table(:existing) 327 drop_if_exists table(:existing) 328 end 329 end 330 331 defmodule NoErrorIndexMigration do 332 use Ecto.Migration 333 334 def change do 335 create_if_not_exists index(:posts, [:title]) 336 create_if_not_exists index(:posts, [:title]) 337 drop_if_exists index(:posts, [:title]) 338 drop_if_exists index(:posts, [:title]) 339 end 340 end 341 342 defmodule InferredDropIndexMigration do 343 use Ecto.Migration 344 345 def change do 346 create index(:posts, [:title]) 347 end 348 end 349 350 defmodule AlterPrimaryKeyMigration do 351 use Ecto.Migration 352 353 def change do 354 create table(:no_pk, primary_key: false) do 355 add :dummy, :string 356 end 357 alter table(:no_pk) do 358 add :id, :serial, primary_key: true 359 end 360 end 361 end 362 363 364 defmodule AddColumnIfNotExistsMigration do 365 use Ecto.Migration 366 367 def up do 368 create table(:add_col_if_not_exists_migration) 369 370 alter table(:add_col_if_not_exists_migration) do 371 add_if_not_exists :value, :integer 372 add_if_not_exists :to_be_added, :integer 373 end 374 375 execute "INSERT INTO add_col_if_not_exists_migration (value, to_be_added) VALUES (1, 2)" 376 end 377 378 def down do 379 drop table(:add_col_if_not_exists_migration) 380 end 381 end 382 383 defmodule DropColumnIfExistsMigration do 384 use Ecto.Migration 385 386 def up do 387 create table(:drop_col_if_exists_migration) do 388 add :value, :integer 389 add :to_be_removed, :integer 390 end 391 392 execute "INSERT INTO drop_col_if_exists_migration (value, to_be_removed) VALUES (1, 2)" 393 394 alter table(:drop_col_if_exists_migration) do 395 remove_if_exists :to_be_removed, :integer 396 end 397 end 398 399 def down do 400 drop table(:drop_col_if_exists_migration) 401 end 402 end 403 404 defmodule NoErrorOnConditionalColumnMigration do 405 use Ecto.Migration 406 407 def up do 408 create table(:no_error_on_conditional_column_migration) 409 410 alter table(:no_error_on_conditional_column_migration) do 411 add_if_not_exists :value, :integer 412 add_if_not_exists :value, :integer 413 414 remove_if_exists :value, :integer 415 remove_if_exists :value, :integer 416 end 417 end 418 419 def down do 420 drop table(:no_error_on_conditional_column_migration) 421 end 422 end 423 424 import Ecto.Query, only: [from: 2] 425 import Ecto.Migrator, only: [up: 4, down: 4] 426 427 # Avoid migration out of order warnings 428 @moduletag :capture_log 429 @base_migration 1_000_000 430 431 setup do 432 {:ok, migration_number: System.unique_integer([:positive]) + @base_migration} 433 end 434 435 test "create and drop table and indexes", %{migration_number: num} do 436 assert :ok == up(PoolRepo, num, CreateMigration, log: false) 437 assert :ok == down(PoolRepo, num, CreateMigration, log: false) 438 end 439 440 test "correctly infers how to drop index", %{migration_number: num} do 441 assert :ok == up(PoolRepo, num, InferredDropIndexMigration, log: false) 442 assert :ok == down(PoolRepo, num, InferredDropIndexMigration, log: false) 443 end 444 445 test "supports on delete", %{migration_number: num} do 446 assert :ok == up(PoolRepo, num, OnDeleteMigration, log: false) 447 448 parent1 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent1") 449 parent2 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent2") 450 451 writer = "INSERT INTO ref_migration (parent1, parent2) VALUES (#{parent1.id}, #{parent2.id})" 452 PoolRepo.query!(writer) 453 454 reader = from r in "ref_migration", select: {r.parent1, r.parent2} 455 assert PoolRepo.all(reader) == [{parent1.id, parent2.id}] 456 457 PoolRepo.delete!(parent1) 458 assert PoolRepo.all(reader) == [{nil, parent2.id}] 459 460 PoolRepo.delete!(parent2) 461 assert PoolRepo.all(reader) == [] 462 463 assert :ok == down(PoolRepo, num, OnDeleteMigration, log: false) 464 end 465 466 test "composite foreign keys", %{migration_number: num} do 467 assert :ok == up(PoolRepo, num, CompositeForeignKeyMigration, log: false) 468 469 PoolRepo.insert_all("composite_parent", [[key_id: 2]]) 470 assert [id] = PoolRepo.all(from p in "composite_parent", select: p.id) 471 472 catch_error(PoolRepo.insert_all("composite_child", [[parent_id: id, parent_key_id: 1]])) 473 assert {1, nil} = PoolRepo.insert_all("composite_child", [[parent_id: id, parent_key_id: 2]]) 474 475 assert :ok == down(PoolRepo, num, CompositeForeignKeyMigration, log: false) 476 end 477 478 test "rolls back references in change/1", %{migration_number: num} do 479 assert :ok == up(PoolRepo, num, ReferencesRollbackMigration, log: false) 480 assert :ok == down(PoolRepo, num, ReferencesRollbackMigration, log: false) 481 end 482 483 test "create table if not exists and drop table if exists does not raise on failure", %{migration_number: num} do 484 assert :ok == up(PoolRepo, num, NoErrorTableMigration, log: false) 485 end 486 487 @tag :create_index_if_not_exists 488 test "create index if not exists and drop index if exists does not raise on failure", %{migration_number: num} do 489 assert :ok == up(PoolRepo, num, NoErrorIndexMigration, log: false) 490 end 491 492 test "raises on NoSQL migrations", %{migration_number: num} do 493 assert_raise ArgumentError, ~r"does not support keyword lists in :options", fn -> 494 up(PoolRepo, num, NoSQLMigration, log: false) 495 end 496 end 497 498 @tag :add_column 499 test "add column", %{migration_number: num} do 500 assert :ok == up(PoolRepo, num, AddColumnMigration, log: false) 501 assert [2] == PoolRepo.all from p in "add_col_migration", select: p.to_be_added 502 :ok = down(PoolRepo, num, AddColumnMigration, log: false) 503 end 504 505 @tag :modify_column 506 test "modify column", %{migration_number: num} do 507 assert :ok == up(PoolRepo, num, AlterColumnMigration, log: false) 508 509 assert ["foo"] == 510 PoolRepo.all from p in "alter_col_migration", select: p.from_null_to_not_null 511 assert [nil] == 512 PoolRepo.all from p in "alter_col_migration", select: p.from_not_null_to_null 513 assert [nil] == 514 PoolRepo.all from p in "alter_col_migration", select: p.from_default_to_no_default 515 assert [0] == 516 PoolRepo.all from p in "alter_col_migration", select: p.from_no_default_to_default 517 518 query = "INSERT INTO alter_col_migration (from_not_null_to_null) VALUES ('foo')" 519 assert catch_error(PoolRepo.query!(query)) 520 521 :ok = down(PoolRepo, num, AlterColumnMigration, log: false) 522 end 523 524 @tag :modify_column 525 test "modify column with from", %{migration_number: num} do 526 assert :ok == up(PoolRepo, num, AlterColumnFromMigration, log: false) 527 528 assert [1] == 529 PoolRepo.all from p in "modify_from_products", select: p.value 530 531 :ok = down(PoolRepo, num, AlterColumnFromMigration, log: false) 532 end 533 534 @tag :alter_primary_key 535 test "modify column with from and pkey", %{migration_number: num} do 536 assert :ok == up(PoolRepo, num, AlterColumnFromPkeyMigration, log: false) 537 538 assert [1] == 539 PoolRepo.all from p in "modify_from_posts", select: p.author_id 540 541 :ok = down(PoolRepo, num, AlterColumnFromPkeyMigration, log: false) 542 end 543 544 @tag :alter_foreign_key 545 test "modify foreign key's on_delete constraint", %{migration_number: num} do 546 assert :ok == up(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false) 547 548 PoolRepo.insert_all("alter_fk_users", [[]]) 549 assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id 550 551 PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]]) 552 PoolRepo.delete_all("alter_fk_users") 553 assert [nil] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id 554 555 :ok = down(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false) 556 end 557 558 @tag :assigns_id_type 559 test "modify foreign key's on_update constraint", %{migration_number: num} do 560 assert :ok == up(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false) 561 562 PoolRepo.insert_all("alter_fk_users", [[]]) 563 assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id 564 565 PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]]) 566 PoolRepo.update_all("alter_fk_users", set: [id: 12345]) 567 assert [12345] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id 568 569 PoolRepo.delete_all("alter_fk_posts") 570 :ok = down(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false) 571 end 572 573 @tag :remove_column 574 test "remove column", %{migration_number: num} do 575 assert :ok == up(PoolRepo, num, DropColumnMigration, log: false) 576 assert catch_error(PoolRepo.all from p in "drop_col_migration", select: p.to_be_removed) 577 :ok = down(PoolRepo, num, DropColumnMigration, log: false) 578 end 579 580 @tag :rename_column 581 test "rename column", %{migration_number: num} do 582 assert :ok == up(PoolRepo, num, RenameColumnMigration, log: false) 583 assert [1] == PoolRepo.all from p in "rename_col_migration", select: p.was_renamed 584 :ok = down(PoolRepo, num, RenameColumnMigration, log: false) 585 end 586 587 @tag :rename_table 588 test "rename table", %{migration_number: num} do 589 assert :ok == up(PoolRepo, num, RenameMigration, log: false) 590 assert :ok == down(PoolRepo, num, RenameMigration, log: false) 591 end 592 593 @tag :prefix 594 test "prefix", %{migration_number: num} do 595 assert :ok == up(PoolRepo, num, PrefixMigration, log: false) 596 assert :ok == down(PoolRepo, num, PrefixMigration, log: false) 597 end 598 599 @tag :alter_primary_key 600 test "alter primary key", %{migration_number: num} do 601 assert :ok == up(PoolRepo, num, AlterPrimaryKeyMigration, log: false) 602 assert :ok == down(PoolRepo, num, AlterPrimaryKeyMigration, log: false) 603 end 604 605 @tag :add_column_if_not_exists 606 @tag :remove_column_if_exists 607 test "add if not exists and remove if exists does not raise on failure", %{migration_number: num} do 608 assert :ok == up(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false) 609 assert :ok == down(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false) 610 end 611 612 @tag :add_column_if_not_exists 613 test "add column if not exists", %{migration_number: num} do 614 assert :ok == up(PoolRepo, num, AddColumnIfNotExistsMigration, log: false) 615 assert [2] == PoolRepo.all from p in "add_col_if_not_exists_migration", select: p.to_be_added 616 :ok = down(PoolRepo, num, AddColumnIfNotExistsMigration, log: false) 617 end 618 619 @tag :remove_column_if_exists 620 test "remove column when exists", %{migration_number: num} do 621 assert :ok == up(PoolRepo, num, DropColumnIfExistsMigration, log: false) 622 assert catch_error(PoolRepo.all from p in "drop_col_if_exists_migration", select: p.to_be_removed) 623 :ok = down(PoolRepo, num, DropColumnIfExistsMigration, log: false) 624 end 625 end