cat-bookmarker/deps/ecto_sql/integration_test/sql/migration.exs

687 lines
19 KiB
Elixir
Raw Normal View History

2024-03-10 18:52:04 +00:00
defmodule Ecto.Integration.MigrationTest do
use ExUnit.Case, async: true
alias Ecto.Integration.{TestRepo, PoolRepo}
defmodule CreateMigration do
use Ecto.Migration
@table table(:create_table_migration)
@index index(:create_table_migration, [:value], unique: true)
def up do
create @table do
add :value, :integer
end
create @index
end
def down do
drop @index
drop @table
end
end
defmodule AddColumnMigration do
use Ecto.Migration
def up do
create table(:add_col_migration) do
add :value, :integer
end
alter table(:add_col_migration) do
add :to_be_added, :integer
end
execute "INSERT INTO add_col_migration (value, to_be_added) VALUES (1, 2)"
end
def down do
drop table(:add_col_migration)
end
end
defmodule AlterColumnMigration do
use Ecto.Migration
def up do
create table(:alter_col_migration) do
add :from_null_to_not_null, :integer
add :from_not_null_to_null, :integer, null: false
add :from_default_to_no_default, :integer, default: 0
add :from_no_default_to_default, :integer
end
alter table(:alter_col_migration) do
modify :from_null_to_not_null, :string, null: false
modify :from_not_null_to_null, :string, null: true
modify :from_default_to_no_default, :integer, default: nil
modify :from_no_default_to_default, :integer, default: 0
end
execute "INSERT INTO alter_col_migration (from_null_to_not_null) VALUES ('foo')"
end
def down do
drop table(:alter_col_migration)
end
end
defmodule AlterColumnFromMigration do
use Ecto.Migration
def change do
create table(:modify_from_products) do
add :value, :integer
add :nullable, :integer, null: false
end
if direction() == :up do
flush()
PoolRepo.insert_all "modify_from_products", [[value: 1, nullable: 1]]
end
alter table(:modify_from_products) do
modify :value, :bigint, from: :integer
modify :nullable, :bigint, null: true, from: {:integer, null: false}
end
end
end
defmodule AlterColumnFromPkeyMigration do
use Ecto.Migration
def change do
create table(:modify_from_authors, primary_key: false) do
add :id, :integer, primary_key: true
end
create table(:modify_from_posts) do
add :author_id, references(:modify_from_authors, type: :integer)
end
if direction() == :up do
flush()
PoolRepo.insert_all "modify_from_authors", [[id: 1]]
PoolRepo.insert_all "modify_from_posts", [[author_id: 1]]
end
alter table(:modify_from_posts) do
# remove the constraints modify_from_posts_author_id_fkey
modify :author_id, :integer, from: references(:modify_from_authors, type: :integer)
end
alter table(:modify_from_authors) do
modify :id, :bigint, from: :integer
end
alter table(:modify_from_posts) do
# add the constraints modify_from_posts_author_id_fkey
modify :author_id, references(:modify_from_authors, type: :bigint), from: :integer
end
end
end
defmodule AlterForeignKeyOnDeleteMigration do
use Ecto.Migration
def up do
create table(:alter_fk_users)
create table(:alter_fk_posts) do
add :alter_fk_user_id, :id
end
alter table(:alter_fk_posts) do
modify :alter_fk_user_id, references(:alter_fk_users, on_delete: :nilify_all)
end
end
def down do
drop table(:alter_fk_posts)
drop table(:alter_fk_users)
end
end
defmodule AlterForeignKeyOnUpdateMigration do
use Ecto.Migration
def up do
create table(:alter_fk_users)
create table(:alter_fk_posts) do
add :alter_fk_user_id, :id
end
alter table(:alter_fk_posts) do
modify :alter_fk_user_id, references(:alter_fk_users, on_update: :update_all)
end
end
def down do
drop table(:alter_fk_posts)
drop table(:alter_fk_users)
end
end
defmodule DropColumnMigration do
use Ecto.Migration
def up do
create table(:drop_col_migration) do
add :value, :integer
add :to_be_removed, :integer
end
execute "INSERT INTO drop_col_migration (value, to_be_removed) VALUES (1, 2)"
alter table(:drop_col_migration) do
remove :to_be_removed
end
end
def down do
drop table(:drop_col_migration)
end
end
defmodule RenameColumnMigration do
use Ecto.Migration
def up do
create table(:rename_col_migration) do
add :to_be_renamed, :integer
end
rename table(:rename_col_migration), :to_be_renamed, to: :was_renamed
execute "INSERT INTO rename_col_migration (was_renamed) VALUES (1)"
end
def down do
drop table(:rename_col_migration)
end
end
defmodule OnDeleteMigration do
use Ecto.Migration
def up do
create table(:parent1)
create table(:parent2)
create table(:ref_migration) do
add :parent1, references(:parent1, on_delete: :nilify_all)
end
alter table(:ref_migration) do
add :parent2, references(:parent2, on_delete: :delete_all)
end
end
def down do
drop table(:ref_migration)
drop table(:parent1)
drop table(:parent2)
end
end
defmodule OnDeleteNilifyColumnsMigration do
use Ecto.Migration
def up do
create table(:parent) do
add :col1, :integer
add :col2, :integer
end
create unique_index(:parent, [:id, :col1, :col2])
create table(:ref) do
add :col1, :integer
add :col2, :integer
add :parent_id,
references(:parent,
with: [col1: :col1, col2: :col2],
on_delete: {:nilify, [:parent_id, :col2]}
)
end
end
def down do
drop table(:ref)
drop table(:parent)
end
end
defmodule CompositeForeignKeyMigration do
use Ecto.Migration
def change do
create table(:composite_parent) do
add :key_id, :integer
end
create unique_index(:composite_parent, [:id, :key_id])
create table(:composite_child) do
add :parent_key_id, :integer
add :parent_id, references(:composite_parent, with: [parent_key_id: :key_id])
end
end
end
defmodule RenameIndexMigration do
use Ecto.Migration
def change do
create table(:composite_parent) do
add :key_id, :integer
end
create unique_index(:composite_parent, [:id, :key_id], name: "old_index_name")
rename index(:composite_parent, [:id, :key_id], name: "old_index_name"), to: "new_index_name"
end
end
defmodule ReferencesRollbackMigration do
use Ecto.Migration
def change do
create table(:parent) do
add :name, :string
end
create table(:child) do
add :parent_id, references(:parent)
end
end
end
defmodule RenameMigration do
use Ecto.Migration
@table_current table(:posts_migration)
@table_new table(:new_posts_migration)
def up do
create @table_current
rename @table_current, to: @table_new
end
def down do
drop @table_new
end
end
defmodule PrefixMigration do
use Ecto.Migration
@prefix "ecto_prefix_test"
def up do
execute TestRepo.create_prefix(@prefix)
create table(:first, prefix: @prefix)
create table(:second, prefix: @prefix) do
add :first_id, references(:first)
end
end
def down do
drop table(:second, prefix: @prefix)
drop table(:first, prefix: @prefix)
execute TestRepo.drop_prefix(@prefix)
end
end
defmodule NoSQLMigration do
use Ecto.Migration
def up do
create table(:collection, options: [capped: true])
execute create: "collection"
end
end
defmodule Parent do
use Ecto.Schema
schema "parent" do
end
end
defmodule NoErrorTableMigration do
use Ecto.Migration
def change do
create_if_not_exists table(:existing) do
add :name, :string
end
create_if_not_exists table(:existing) do
add :name, :string
end
create_if_not_exists table(:existing)
drop_if_exists table(:existing)
drop_if_exists table(:existing)
end
end
defmodule NoErrorIndexMigration do
use Ecto.Migration
def change do
create_if_not_exists index(:posts, [:title])
create_if_not_exists index(:posts, [:title])
drop_if_exists index(:posts, [:title])
drop_if_exists index(:posts, [:title])
end
end
defmodule InferredDropIndexMigration do
use Ecto.Migration
def change do
create index(:posts, [:title])
end
end
defmodule AlterPrimaryKeyMigration do
use Ecto.Migration
def change do
create table(:no_pk, primary_key: false) do
add :dummy, :string
end
alter table(:no_pk) do
add :id, :serial, primary_key: true
end
end
end
defmodule AddColumnIfNotExistsMigration do
use Ecto.Migration
def up do
create table(:add_col_if_not_exists_migration)
alter table(:add_col_if_not_exists_migration) do
add_if_not_exists :value, :integer
add_if_not_exists :to_be_added, :integer
end
execute "INSERT INTO add_col_if_not_exists_migration (value, to_be_added) VALUES (1, 2)"
end
def down do
drop table(:add_col_if_not_exists_migration)
end
end
defmodule DropColumnIfExistsMigration do
use Ecto.Migration
def up do
create table(:drop_col_if_exists_migration) do
add :value, :integer
add :to_be_removed, :integer
end
execute "INSERT INTO drop_col_if_exists_migration (value, to_be_removed) VALUES (1, 2)"
alter table(:drop_col_if_exists_migration) do
remove_if_exists :to_be_removed, :integer
end
end
def down do
drop table(:drop_col_if_exists_migration)
end
end
defmodule NoErrorOnConditionalColumnMigration do
use Ecto.Migration
def up do
create table(:no_error_on_conditional_column_migration)
alter table(:no_error_on_conditional_column_migration) do
add_if_not_exists :value, :integer
add_if_not_exists :value, :integer
remove_if_exists :value, :integer
remove_if_exists :value, :integer
end
end
def down do
drop table(:no_error_on_conditional_column_migration)
end
end
import Ecto.Query, only: [from: 2]
import Ecto.Migrator, only: [up: 4, down: 4]
# Avoid migration out of order warnings
@moduletag :capture_log
@base_migration 1_000_000
setup do
{:ok, migration_number: System.unique_integer([:positive]) + @base_migration}
end
test "create and drop table and indexes", %{migration_number: num} do
assert :ok == up(PoolRepo, num, CreateMigration, log: false)
assert :ok == down(PoolRepo, num, CreateMigration, log: false)
end
test "correctly infers how to drop index", %{migration_number: num} do
assert :ok == up(PoolRepo, num, InferredDropIndexMigration, log: false)
assert :ok == down(PoolRepo, num, InferredDropIndexMigration, log: false)
end
test "supports on delete", %{migration_number: num} do
assert :ok == up(PoolRepo, num, OnDeleteMigration, log: false)
parent1 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent1")
parent2 = PoolRepo.insert! Ecto.put_meta(%Parent{}, source: "parent2")
writer = "INSERT INTO ref_migration (parent1, parent2) VALUES (#{parent1.id}, #{parent2.id})"
PoolRepo.query!(writer)
reader = from r in "ref_migration", select: {r.parent1, r.parent2}
assert PoolRepo.all(reader) == [{parent1.id, parent2.id}]
PoolRepo.delete!(parent1)
assert PoolRepo.all(reader) == [{nil, parent2.id}]
PoolRepo.delete!(parent2)
assert PoolRepo.all(reader) == []
assert :ok == down(PoolRepo, num, OnDeleteMigration, log: false)
end
test "composite foreign keys", %{migration_number: num} do
assert :ok == up(PoolRepo, num, CompositeForeignKeyMigration, log: false)
PoolRepo.insert_all("composite_parent", [[key_id: 2]])
assert [id] = PoolRepo.all(from p in "composite_parent", select: p.id)
catch_error(PoolRepo.insert_all("composite_child", [[parent_id: id, parent_key_id: 1]]))
assert {1, nil} = PoolRepo.insert_all("composite_child", [[parent_id: id, parent_key_id: 2]])
assert :ok == down(PoolRepo, num, CompositeForeignKeyMigration, log: false)
end
test "rename index", %{migration_number: num} do
assert :ok == up(PoolRepo, num, RenameIndexMigration, log: false)
assert :ok == down(PoolRepo, num, RenameIndexMigration, log: false)
end
test "rolls back references in change/1", %{migration_number: num} do
assert :ok == up(PoolRepo, num, ReferencesRollbackMigration, log: false)
assert :ok == down(PoolRepo, num, ReferencesRollbackMigration, log: false)
end
test "create table if not exists and drop table if exists does not raise on failure", %{migration_number: num} do
assert :ok == up(PoolRepo, num, NoErrorTableMigration, log: false)
end
@tag :create_index_if_not_exists
test "create index if not exists and drop index if exists does not raise on failure", %{migration_number: num} do
assert :ok == up(PoolRepo, num, NoErrorIndexMigration, log: false)
end
test "raises on NoSQL migrations", %{migration_number: num} do
assert_raise ArgumentError, ~r"does not support keyword lists in :options", fn ->
up(PoolRepo, num, NoSQLMigration, log: false)
end
end
@tag :add_column
test "add column", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AddColumnMigration, log: false)
assert [2] == PoolRepo.all from p in "add_col_migration", select: p.to_be_added
:ok = down(PoolRepo, num, AddColumnMigration, log: false)
end
@tag :modify_column
test "modify column", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterColumnMigration, log: false)
assert ["foo"] ==
PoolRepo.all from p in "alter_col_migration", select: p.from_null_to_not_null
assert [nil] ==
PoolRepo.all from p in "alter_col_migration", select: p.from_not_null_to_null
assert [nil] ==
PoolRepo.all from p in "alter_col_migration", select: p.from_default_to_no_default
assert [0] ==
PoolRepo.all from p in "alter_col_migration", select: p.from_no_default_to_default
query = "INSERT INTO `alter_col_migration` (\"from_not_null_to_null\") VALUES ('foo')"
assert catch_error(PoolRepo.query!(query))
:ok = down(PoolRepo, num, AlterColumnMigration, log: false)
end
@tag :modify_column
test "modify column with from", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterColumnFromMigration, log: false)
assert [1] ==
PoolRepo.all from p in "modify_from_products", select: p.value
:ok = down(PoolRepo, num, AlterColumnFromMigration, log: false)
end
@tag :alter_primary_key
test "modify column with from and pkey", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterColumnFromPkeyMigration, log: false)
assert [1] ==
PoolRepo.all from p in "modify_from_posts", select: p.author_id
:ok = down(PoolRepo, num, AlterColumnFromPkeyMigration, log: false)
end
@tag :alter_foreign_key
test "modify foreign key's on_delete constraint", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false)
PoolRepo.insert_all("alter_fk_users", [[]])
assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id
PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]])
PoolRepo.delete_all("alter_fk_users")
assert [nil] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id
:ok = down(PoolRepo, num, AlterForeignKeyOnDeleteMigration, log: false)
end
@tag :assigns_id_type
test "modify foreign key's on_update constraint", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false)
PoolRepo.insert_all("alter_fk_users", [[]])
assert [id] = PoolRepo.all from p in "alter_fk_users", select: p.id
PoolRepo.insert_all("alter_fk_posts", [[alter_fk_user_id: id]])
PoolRepo.update_all("alter_fk_users", set: [id: 12345])
assert [12345] == PoolRepo.all from p in "alter_fk_posts", select: p.alter_fk_user_id
PoolRepo.delete_all("alter_fk_posts")
:ok = down(PoolRepo, num, AlterForeignKeyOnUpdateMigration, log: false)
end
@tag :remove_column
test "remove column", %{migration_number: num} do
assert :ok == up(PoolRepo, num, DropColumnMigration, log: false)
assert catch_error(PoolRepo.all from p in "drop_col_migration", select: p.to_be_removed)
:ok = down(PoolRepo, num, DropColumnMigration, log: false)
end
@tag :rename_column
test "rename column", %{migration_number: num} do
assert :ok == up(PoolRepo, num, RenameColumnMigration, log: false)
assert [1] == PoolRepo.all from p in "rename_col_migration", select: p.was_renamed
:ok = down(PoolRepo, num, RenameColumnMigration, log: false)
end
@tag :rename_table
test "rename table", %{migration_number: num} do
assert :ok == up(PoolRepo, num, RenameMigration, log: false)
assert :ok == down(PoolRepo, num, RenameMigration, log: false)
end
@tag :prefix
test "prefix", %{migration_number: num} do
assert :ok == up(PoolRepo, num, PrefixMigration, log: false)
assert :ok == down(PoolRepo, num, PrefixMigration, log: false)
end
@tag :alter_primary_key
test "alter primary key", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AlterPrimaryKeyMigration, log: false)
assert :ok == down(PoolRepo, num, AlterPrimaryKeyMigration, log: false)
end
@tag :add_column_if_not_exists
@tag :remove_column_if_exists
test "add if not exists and remove if exists does not raise on failure", %{migration_number: num} do
assert :ok == up(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false)
assert :ok == down(PoolRepo, num, NoErrorOnConditionalColumnMigration, log: false)
end
@tag :add_column_if_not_exists
test "add column if not exists", %{migration_number: num} do
assert :ok == up(PoolRepo, num, AddColumnIfNotExistsMigration, log: false)
assert [2] == PoolRepo.all from p in "add_col_if_not_exists_migration", select: p.to_be_added
:ok = down(PoolRepo, num, AddColumnIfNotExistsMigration, log: false)
end
@tag :remove_column_if_exists
test "remove column when exists", %{migration_number: num} do
assert :ok == up(PoolRepo, num, DropColumnIfExistsMigration, log: false)
assert catch_error(PoolRepo.all from p in "drop_col_if_exists_migration", select: p.to_be_removed)
:ok = down(PoolRepo, num, DropColumnIfExistsMigration, log: false)
end
@tag :on_delete_nilify_column_list
test "nilify list of columns on_delete constraint", %{migration_number: num} do
assert :ok == up(PoolRepo, num, OnDeleteNilifyColumnsMigration, log: false)
PoolRepo.insert_all("parent", [%{col1: 1, col2: 2}])
assert [{id, col1, col2}] = PoolRepo.all from p in "parent", select: {p.id, p.col1, p.col2}
PoolRepo.insert_all("ref", [[parent_id: id, col1: col1, col2: col2]])
PoolRepo.delete_all("parent")
assert [{nil, col1, nil}] == PoolRepo.all from r in "ref", select: {r.parent_id, r.col1, r.col2}
:ok = down(PoolRepo, num, OnDeleteNilifyColumnsMigration, log: false)
end
end