Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 118 additions & 46 deletions lib/yacto/migration/gen_migration.ex
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
defmodule Yacto.Migration.GenMigration do
require Logger

defp convert_fields(types, attrs) do
defmodule GenMigrationError do
defexception [:message, :from, :to]
def message(error) do
"#{error.message} from: #{inspect error.from} to: #{inspect error.to}"
end
end

defp convert_fields(types, attrs, primary_keys, autogenerate_id) do
# types:
# %{del: %{field: type},
# ins: %{field: type}}
# attrs:
# %{ins: %{field: attr},
# del: %{field: attr}}
# primary_keys:
# [eq: [field]]
# or
# [ins: [field]]
# autogenerate_id:
# :not_changed
# or
# {:create, {ecto_schema_field_name, database_field_name, type}}
# result:
# [{field, {:add, {type, attr}} |
# [{field, {:add, {type, attr, is_primary_key, is_autogenerate}} |
# :remove |
# {:modify, attr}}]
# {:modify, attr, is_primary_key, is_autogenerate}}]

# get all field names
type_fields =
Expand All @@ -26,13 +41,23 @@ defmodule Yacto.Migration.GenMigration do
field
end

fields = type_fields ++ attr_fields
primary_key_fields = Keyword.get(primary_keys, :ins, [])

autogenerate_field =
case autogenerate_id do
{:create, {_, field, _}} -> [field]
:not_changed -> []
end

fields = type_fields ++ attr_fields ++ primary_key_fields ++ autogenerate_field
fields = fields |> Enum.sort() |> Enum.dedup()

changes =
for field <- fields do
in_type_del = Map.has_key?(types.del, field)
in_type_ins = Map.has_key?(types.ins, field)
is_primary_key = Enum.member?(primary_key_fields, field)
is_autogenerate = Enum.member?(autogenerate_field, field)

cond do
in_type_del && in_type_ins ->
Expand All @@ -46,7 +71,7 @@ defmodule Yacto.Migration.GenMigration do
[]
end

[{field, :remove}, {field, {:add, type, attr}}]
[{field, :remove}, {field, {:add, type, attr, is_primary_key, is_autogenerate}}]

in_type_del && !in_type_ins ->
# :remove
Expand All @@ -63,7 +88,7 @@ defmodule Yacto.Migration.GenMigration do
[]
end

[{field, {:add, type, attr}}]
[{field, {:add, type, attr, is_primary_key, is_autogenerate}}]

!in_type_del && !in_type_ins ->
# :modify
Expand All @@ -75,55 +100,58 @@ defmodule Yacto.Migration.GenMigration do
[]
end

[{field, {:modify, attr}}]
[{field, {:modify, attr, is_primary_key, is_autogenerate}}]
end
end

List.flatten(changes)
end

def generate_fields(types, attrs, structure_to, _migration_opts) do
ops = convert_fields(types, attrs)
def generate_fields(types, attrs, primary_keys, autogenerate_id, structure_to, _migration_opts) do
ops = convert_fields(types, attrs, primary_keys, autogenerate_id)

{remove_ops, other_ops} = Enum.split_with(ops, fn {_, op} -> op == :remove end)

lines =
for {field, op} <- ops do
case op do
{:add, type, attr} ->
opts = attr

is_primary_key = Enum.find(structure_to.primary_key, &(&1 == field)) != nil
opts = opts ++ if(is_primary_key, do: [primary_key: true], else: [])

is_autogenerate =
if(
structure_to.autogenerate_id,
do: elem(structure_to.autogenerate_id, 0) == field,
else: false
)

opts = opts ++ if(is_autogenerate, do: [autogenerate: true], else: [])

[" add(:#{field}, #{inspect(type)}, #{inspect(opts)})"]

:remove ->
lines = [" remove(:#{field})"]

if field == :id do
[" add(:_gen_migration_dummy, :integer, [])"] ++
lines ++
["end"] ++
["alter table(#{inspect(structure_to.source)}) do"] ++
[" remove(:_gen_migration_dummy)"]
else
lines
end
if length(remove_ops) > 0 do
lines =
for {field, :remove} <- remove_ops do
[" remove(:#{field})"]
end

{:modify, attr} ->
type = Map.fetch!(structure_to.types, field)
[" modify(:#{field}, :#{type}, #{inspect(attr)})"]
end
[" add(:_gen_migration_dummy, :integer, [])"] ++
lines ++
["end"] ++
["alter table(#{inspect(structure_to.source)}) do"] ++
[" remove(:_gen_migration_dummy)"]
else
[]
end

lines =
lines ++
for {field, op} <- other_ops do
case op do
{:add, type, attr, is_primary_key, is_autogenerate} ->
opts = attr

opts = opts ++ if(is_primary_key, do: [primary_key: true], else: [])

type = if type == :id && is_autogenerate, do: :bigserial, else: type

[" add(:#{field}, #{inspect(type)}, #{inspect(opts)})"]

{:modify, attr, is_primary_key, is_autogenerate} ->
type = Map.fetch!(structure_to.types, field)
opts = attr
opts = opts ++ if(is_primary_key, do: [primary_key: true], else: [])

type = if type == :id && is_autogenerate, do: :bigserial, else: type

[" modify(:#{field}, :#{type}, #{inspect(opts)})"]
end
end

List.flatten(lines)
end

Expand Down Expand Up @@ -389,6 +417,39 @@ defmodule Yacto.Migration.GenMigration do
diff = Yacto.Migration.Structure.diff(structure_from, structure_to)
rdiff = Yacto.Migration.Structure.diff(structure_to, structure_from)

case {structure_from.primary_key, structure_to.primary_key} do
{[], []} ->
:ok

{[], _to} ->
:ok

{_from, []} ->
:ok

# TODO このロジックテストされてないのでテスト追加する
{from, to} when from == to ->
case diff.autogenerate_id do
:not_changed ->
:ok

{:create, _to_value} ->
:ok

_ ->
raise Yacto.Migration.GenMigration.GenMigrationError,
message: "AutogenerateID Primary key can not be changed",
from: structure_from,
to: structure_to
end

{from, to} when from != to ->
raise Yacto.Migration.GenMigration.GenMigrationError,
message: "Primary key can not be changed",
from: structure_from,
to: structure_to
end

if diff == rdiff do
:not_changed
else
Expand All @@ -404,7 +465,11 @@ defmodule Yacto.Migration.GenMigration do
["drop table(#{inspect(from_value)})"]

{:create, _to_value} ->
["create table(#{inspect(structure_to.source)})"]
[
"create table(#{inspect(structure_to.source)}, primary_key: false) do",
" add(:id, :id)",
"end"
]
end

lines =
Expand All @@ -415,7 +480,14 @@ defmodule Yacto.Migration.GenMigration do

_ ->
["alter table(#{inspect(structure_to.source)}) do"] ++
generate_fields(diff.types, diff.meta.attrs, structure_to, migration_opts) ++
generate_fields(
diff.types,
diff.meta.attrs,
diff.primary_key,
diff.autogenerate_id,
structure_to,
migration_opts
) ++
["end"] ++ generate_indices(diff.meta.indices, structure_to, migration_opts)
end

Expand Down
4 changes: 2 additions & 2 deletions lib/yacto/migration/migrator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ defmodule Yacto.Migration.Migrator do
end

defp run(repo, schema, migration, direction, operation, migrator_direction, opts) do
level = Keyword.get(opts, :log, :info)
sql = Keyword.get(opts, :log_sql, false)
level = Keyword.get(opts, :log, :debug)
sql = Keyword.get(opts, :log_sql, true)
log = %{level: level, sql: sql}
args = [self(), repo, direction, migrator_direction, log]

Expand Down
4 changes: 2 additions & 2 deletions lib/yacto/migration/structure.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
defmodule Yacto.Migration.Structure do
defstruct source: nil,
prefix: nil,
primary_key: [:id],
primary_key: [],
fields: [:id],
field_sources: %{id: :id},
types: %{id: :id},
associations: [],
embeds: [],
read_after_writes: [],
autogenerate_id: {:id, :id, :id},
autogenerate_id: nil,
meta: %{attrs: %{}, indices: %{}}

# undocumented keys:
Expand Down
11 changes: 7 additions & 4 deletions test/apps/custom_table_name/test/custom_table_name_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ defmodule CustomTableNameTest do
use Ecto.Migration

def change(CustomTableName.Player.Schema.TestData) do
create table("customtablename_player")
alter table("customtablename_player") do
add(:name, :string, [default: "hoge", null: false, size: 100])
create table(\"customtablename_player\", primary_key: false) do
add(:id, :id)
end
alter table(\"customtablename_player\") do
modify(:id, :bigserial, [primary_key: true])
add(:name, :string, [default: \"hoge\", null: false, size: 100])
end
end

Expand All @@ -18,7 +21,7 @@ defmodule CustomTableNameTest do

def __migration_structures__() do
[
{CustomTableName.Player.Schema.TestData, %Yacto.Migration.Structure{field_sources: %{id: :id, name: :name}, fields: [:id, :name], meta: %{attrs: %{name: %{default: "hoge", null: false, size: 100}}, indices: %{}}, source: "customtablename_player", types: %{id: :id, name: :string}}},
{CustomTableName.Player.Schema.TestData, %Yacto.Migration.Structure{autogenerate_id: {:id, :id, :id}, field_sources: %{id: :id, name: :name}, fields: [:id, :name], meta: %{attrs: %{name: %{default: \"hoge\", null: false, size: 100}}, indices: %{}}, primary_key: [:id], source: \"customtablename_player\", types: %{id: :id, name: :string}}},
]
end

Expand Down
16 changes: 16 additions & 0 deletions test/apps/gen_migration/lib/gen_migration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,22 @@ defmodule GenMigration.Player3 do
end
end

defmodule GenMigration.Player4 do
use Yacto.Schema

@impl Yacto.Schema
def dbname(), do: :player

@primary_key {:id, :binary_id, autogenerate: true}

schema @auto_source do
field(:name3, :string, meta: [null: false, size: 100])
field(:value, :string)
index([:value, :name3])
index([:name3, :value], unique: true)
end
end

defmodule GenMigration.Item do
use Yacto.Schema

Expand Down
Loading