Packages
ash_postgres
2.8.0
2.10.0
2.9.1
2.9.0
2.8.0
2.7.0
2.6.32
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.12
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.22
2.5.21
2.5.20
2.5.19
2.5.18
2.5.17
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.19
2.1.18
2.1.17
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.5.30
1.5.29
1.5.28
1.5.27
1.5.26
1.5.25
1.5.24
1.5.23
1.5.22
1.5.21
1.5.20
1.5.19
1.5.18
1.5.17
1.5.16
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.4.0
1.3.68
1.3.67
1.3.66
1.3.65
1.3.64
1.3.63
1.3.62
1.3.61
1.3.60
1.3.59
1.3.58
1.3.56
1.3.55
1.3.54
1.3.53
1.3.52
1.3.51
1.3.50
1.3.49
1.3.48
1.3.47
1.3.46
1.3.45
1.3.44
1.3.43
1.3.42
1.3.41
1.3.40
1.3.39
1.3.38
1.3.37
1.3.36
1.3.35
1.3.34
1.3.33
1.3.32
1.3.31
1.3.30
1.3.29
1.3.28
1.3.26
1.3.25
1.3.24
1.3.23
1.3.22
1.3.21
1.3.20
1.3.19
1.3.18
1.3.17
1.3.16
1.3.15
1.3.14
1.3.12
1.3.11
1.3.10
1.3.9
1.3.8
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-rc.4
1.3.0-rc.3
1.3.0-rc.2
1.3.0-rc.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.1
1.2.0-rc.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.9
1.0.0-rc.8
1.0.0-rc.7
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
1.0.0-pre.3
1.0.0-pre.2
1.0.0-pre.1
1.0.0-pre.0
0.43.0
0.42.0-rc.7
0.42.0-rc.6
0.42.0-rc.5
0.42.0-rc.4
0.42.0-rc.3
0.42.0-rc.1
0.42.0-rc.0
0.41.7
0.41.6
0.41.5
0.41.4
0.41.3
0.41.2
0.41.1
0.41.0-rc0
retired
0.41.0-rc.9
0.41.0-rc.8
0.41.0-rc.7
0.41.0-rc.6
0.41.0-rc.5
0.41.0-rc.4
0.41.0-rc.3
0.41.0-rc.2
0.41.0-rc.1
0.41.0-rc.0
0.40.11
0.40.10
0.40.9
0.40.8
0.40.7
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0-rc5
0.40.0-rc4
0.40.0-rc3
0.40.0-rc2
0.40.0-rc1
0.39.0-rc0
0.38.11
0.38.10
0.38.9
0.38.8
0.38.7
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.8
0.37.7
0.37.6
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.36.5
0.36.4
0.36.3
0.36.2
0.36.1
0.36.0
0.35.5
0.35.4
0.35.3
0.35.1
0.34.5
0.34.4
0.34.3
0.34.2
0.34.1
0.34.0
0.33.1
0.33.0
0.32.2
0.32.1
0.32.0
0.31.1
0.30.1
0.29.6
0.29.5
0.29.4
0.29.3
0.29.2
0.29.1
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.5
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.1
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.1
0.2.0
0.1.4
0.1.3
0.1.2
0.1.1
0.1.1-rc.2
0.1.1-rc.1
0.1.1-rc.0
0.1.0
The PostgreSQL data layer for Ash Framework
Current section
Files
Jump to
Current section
Files
lib/migration_generator/operation.ex
# SPDX-FileCopyrightText: 2019 ash_postgres contributors <https://github.com/ash-project/ash_postgres/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule AshPostgres.MigrationGenerator.Operation do
@moduledoc false
defmodule Helper do
@moduledoc false
def join(list),
do:
list
|> List.flatten()
|> Enum.reject(&is_nil/1)
|> Enum.join(", ")
|> String.replace(", )", ")")
def maybe_add_default("nil"), do: nil
def maybe_add_default(value), do: "default: #{value}"
def maybe_add_primary_key(true), do: "primary_key: true"
def maybe_add_primary_key(_), do: nil
def maybe_add_null(false), do: "null: false"
def maybe_add_null(_), do: nil
def maybe_add_prefix(nil), do: nil
def maybe_add_prefix(prefix), do: "prefix: #{prefix}"
def in_quotes(nil), do: nil
def in_quotes(value), do: "\"#{value}\""
def as_atom(value) when is_atom(value), do: Macro.inspect_atom(:remote_call, value)
# sobelow_skip ["DOS.StringToAtom"]
def as_atom(value), do: Macro.inspect_atom(:remote_call, String.to_atom(value))
def option(key, value) when key in [:nulls_distinct, "nulls_distinct"] do
if !value do
"#{as_atom(key)}: #{inspect(value)}"
end
end
def option(key, value) do
if value do
"#{as_atom(key)}: #{inspect(value)}"
end
end
def on_delete(%{on_delete: {:nilify, columns}}) when is_list(columns) do
"on_delete: {:nilify, #{inspect(columns)}}"
end
def on_delete(%{on_delete: on_delete}) when on_delete in [:delete, :nilify] do
"on_delete: :#{on_delete}_all"
end
def on_delete(%{on_delete: on_delete}) when is_atom(on_delete) and not is_nil(on_delete) do
"on_delete: :#{on_delete}"
end
def on_delete(_), do: nil
def on_update(%{on_update: on_update}) when on_update in [:update, :nilify] do
"on_update: :#{on_update}_all"
end
def on_update(%{on_update: on_update}) when is_atom(on_update) and not is_nil(on_update) do
"on_update: :#{on_update}"
end
def on_update(_), do: nil
def reference_type(
%{type: :integer},
%{destination_attribute_generated: true, destination_attribute_default: "nil"}
) do
:bigint
end
def reference_type(%{type: :integer, default: "nil", generated?: true}, _) do
":bigserial"
end
def reference_type(%{type: type}, _) do
type
end
def with_match(reference, source_attribute \\ nil)
def with_match(
%{
primary_key?: false,
destination_attribute: reference_attribute,
multitenancy: %{strategy: :attribute, attribute: destination_attribute}
} = reference,
source_attribute
)
when not is_nil(source_attribute) and reference_attribute != destination_attribute do
with_targets =
[{as_atom(source_attribute), as_atom(destination_attribute)}]
|> Enum.into(reference.match_with || %{})
|> with_targets()
# We can only have match: :full here, this gets validated by a Transformer
join([with_targets, "match: :full"])
end
def with_match(reference, _) do
with_targets = with_targets(reference.match_with)
match_type = match_type(reference.match_type)
if with_targets != nil or match_type != nil do
join([with_targets, match_type])
else
nil
end
end
def with_targets(targets) when is_map(targets) do
targets_string =
targets
|> Enum.map_join(", ", fn {source, destination} -> "#{source}: :#{destination}" end)
"with: [#{targets_string}]"
end
def with_targets(_), do: nil
def match_type(type) when type in [:simple, :partial, :full] do
"match: :#{type}"
end
def match_type(_), do: nil
def index_keys(keys, all_tenants?, multitenancy) do
if multitenancy.strategy == :attribute and not all_tenants? do
Enum.uniq([multitenancy.attribute | keys])
else
keys
end
end
def maybe_add_precision(nil), do: nil
def maybe_add_precision(precision), do: "precision: #{precision}"
def maybe_add_scale(nil), do: nil
def maybe_add_scale(scale), do: "scale: #{scale}"
end
defmodule CreateTable do
@moduledoc false
defstruct [:table, :schema, :multitenancy, :old_multitenancy, :repo, :create_table_options]
end
defmodule AddAttribute do
@moduledoc false
defstruct [:attribute, :table, :schema, :multitenancy, :old_multitenancy]
import Helper
def up(%{
multitenancy: %{strategy: :attribute, attribute: source_attribute},
attribute:
%{
references:
%{
table: table,
destination_attribute: reference_attribute,
schema: destination_schema,
multitenancy: %{strategy: :attribute}
} = reference
} = attribute
}) do
with_match = with_match(reference, source_attribute)
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.source)}",
"references(:#{as_atom(table)}",
[
"column: #{inspect(reference_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference)
],
")",
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
multitenancy: %{strategy: :context},
attribute:
%{
references:
%{
table: table,
destination_attribute: destination_attribute,
schema: destination_schema,
multitenancy: %{strategy: :attribute}
} = reference
} = attribute
}) do
with_match = with_match(reference)
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.source)}",
"references(:#{as_atom(table)}",
[
"column: #{inspect(destination_attribute)}",
with_match,
option("prefix", destination_schema),
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
on_delete(reference),
on_update(reference)
],
")",
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
multitenancy: %{strategy: :attribute},
attribute:
%{
references: %{
multitenancy: %{strategy: :context}
}
} = attribute
}) do
size =
if attribute.size do
"size: #{attribute.size}"
end
[
"add #{inspect(attribute.source)}",
inspect(attribute.type),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
multitenancy: %{strategy: nil},
attribute:
%{
references: %{
multitenancy: %{strategy: :context}
}
} = attribute
}) do
size =
if attribute.size do
"size: #{attribute.size}"
end
[
"add #{inspect(attribute.source)}",
inspect(attribute.type),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
multitenancy: %{strategy: :context},
attribute:
%{
references:
%{
multitenancy: %{strategy: :context},
table: table,
destination_attribute: destination_attribute
} = reference
} = attribute
}) do
with_match = with_match(reference)
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.source)}",
"references(:#{as_atom(table)}",
[
"column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
"prefix: prefix()",
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
on_delete(reference),
on_update(reference)
],
")",
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
multitenancy: %{strategy: :context},
schema: schema,
attribute:
%{
references:
%{
table: table,
schema: destination_schema,
destination_attribute: destination_attribute
} = reference
} = attribute
}) do
with_match = with_match(reference)
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
destination_schema =
if schema != destination_schema do
destination_schema
end
[
"add #{inspect(attribute.source)}",
"references(:#{as_atom(table)}",
[
"column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference)
],
")",
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{
attribute:
%{
references:
%{
table: table,
schema: destination_schema,
destination_attribute: destination_attribute
} = reference
} = attribute
}) do
with_match = with_match(reference)
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.source)}",
"references(:#{as_atom(table)}",
[
"column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference)
],
")",
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_default(attribute.default),
maybe_add_primary_key(attribute.primary_key?),
maybe_add_null(attribute.allow_nil?)
]
|> join()
end
def up(%{attribute: %{type: :bigint, default: "nil", generated?: true} = attribute}) do
[
"add #{inspect(attribute.source)}",
":bigserial",
maybe_add_null(attribute.allow_nil?),
maybe_add_primary_key(attribute.primary_key?)
]
|> join()
end
def up(%{attribute: %{type: :integer, default: "nil", generated?: true} = attribute}) do
[
"add #{inspect(attribute.source)}",
":serial",
maybe_add_null(attribute.allow_nil?),
maybe_add_primary_key(attribute.primary_key?)
]
|> join()
end
def up(%{attribute: attribute}) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
[
"add #{inspect(attribute.source)}",
"#{inspect(attribute.type)}",
maybe_add_null(attribute.allow_nil?),
maybe_add_default(attribute.default),
size,
maybe_add_precision(attribute[:precision]),
maybe_add_scale(attribute[:scale]),
maybe_add_primary_key(attribute.primary_key?)
]
|> join()
end
def down(
%{
attribute: attribute,
table: table,
multitenancy: multitenancy
} = op
) do
AshPostgres.MigrationGenerator.Operation.RemoveAttribute.up(%{
op
| attribute: attribute,
table: table,
multitenancy: multitenancy
})
end
end
defmodule AlterDeferrability do
@moduledoc false
defstruct [:table, :schema, :references, :direction, no_phase: true]
defp prefix_name(name, prefix) do
if prefix do
"#{prefix}.#{name}"
else
name
end
end
def up(%{
direction: :up,
schema: schema,
table: table,
references: %{name: name, deferrable: true}
}) do
"execute(\"ALTER TABLE #{prefix_name(table, schema)} ALTER CONSTRAINT #{name} DEFERRABLE INITIALLY IMMEDIATE\");"
end
def up(%{
direction: :up,
schema: schema,
table: table,
references: %{name: name, deferrable: :initially}
}) do
"execute(\"ALTER TABLE #{prefix_name(table, schema)} ALTER CONSTRAINT #{name} DEFERRABLE INITIALLY DEFERRED\");"
end
def up(%{direction: :up, schema: schema, table: table, references: %{name: name}}) do
"execute(\"ALTER TABLE #{prefix_name(table, schema)} ALTER CONSTRAINT #{name} NOT DEFERRABLE\");"
end
def up(_), do: ""
def down(%{direction: :down} = data), do: up(%{data | direction: :up})
def down(_), do: ""
end
defmodule AlterAttribute do
@moduledoc false
defstruct [
:old_attribute,
:new_attribute,
:table,
:schema,
:multitenancy,
:old_multitenancy
]
import Helper
defp alter_opts(attribute, old_attribute) do
primary_key =
cond do
attribute.primary_key? and !old_attribute.primary_key? ->
", primary_key: true"
old_attribute.primary_key? and !attribute.primary_key? ->
", primary_key: false"
true ->
nil
end
default =
if attribute.default != old_attribute.default do
if is_nil(attribute.default) do
", default: nil"
else
", default: #{attribute.default}"
end
end
null =
if attribute.allow_nil? != old_attribute.allow_nil? do
", null: #{attribute.allow_nil?}"
end
size =
if attribute[:size] &&
(Map.get(attribute, :size) != Map.get(old_attribute, :size) ||
attribute.type != old_attribute.type) do
", size: #{attribute.size}"
end
precision =
if Map.get(attribute, :precision) != Map.get(old_attribute, :precision) do
if attribute.precision do
", precision: #{attribute.precision}"
end
end
scale =
if Map.get(attribute, :scale) != Map.get(old_attribute, :scale) do
if attribute.scale do
", scale: #{attribute.scale}"
end
end
"#{null}#{default}#{size}#{precision}#{scale}#{primary_key}"
end
def up(%{
multitenancy: multitenancy,
old_attribute: old_attribute,
new_attribute: attribute,
schema: schema
}) do
type_or_reference =
if AshPostgres.MigrationGenerator.has_reference?(multitenancy, attribute) and
Map.get(old_attribute, :references) != Map.get(attribute, :references) do
reference(multitenancy, attribute, schema)
else
if attribute.type == :bigint and attribute.default == "nil" and attribute.generated? do
":bigserial"
else
inspect(attribute.type)
end
end
"modify #{inspect(attribute.source)}, #{type_or_reference}#{alter_opts(attribute, old_attribute)}"
end
defp reference(
%{strategy: :context},
%{
references:
%{
multitenancy: %{strategy: :context},
table: table,
destination_attribute: destination_attribute
} = reference
} = attribute,
_schema
) do
with_match = with_match(reference)
join([
"references(:#{as_atom(table)}, column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
"prefix: prefix()",
on_delete(reference),
on_update(reference),
")"
])
end
defp reference(
%{strategy: :attribute, attribute: source_attribute},
%{
references:
%{
multitenancy: %{strategy: :attribute},
table: table,
schema: destination_schema,
destination_attribute: reference_attribute
} = reference
} = attribute,
schema
) do
destination_schema =
if schema != destination_schema do
destination_schema
end
with_match = with_match(reference, source_attribute)
join([
"references(:#{as_atom(table)}, column: #{inspect(reference_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference),
")"
])
end
defp reference(
%{strategy: :context},
%{
references:
%{
table: table,
destination_attribute: destination_attribute,
schema: destination_schema
} = reference
} = attribute,
schema
) do
with_match = with_match(reference)
destination_schema =
if schema != destination_schema do
destination_schema
end
join([
"references(:#{as_atom(table)}, column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference),
")"
])
end
defp reference(
_,
%{
references:
%{
table: table,
destination_attribute: destination_attribute,
schema: destination_schema
} = reference
} = attribute,
schema
) do
with_match = with_match(reference)
destination_schema =
if schema != destination_schema do
destination_schema
end
join([
"references(:#{as_atom(table)}, column: #{inspect(destination_attribute)}",
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference),
")"
])
end
def down(op) do
up(%{
op
| old_attribute: op.new_attribute,
new_attribute: Map.put(op.old_attribute, :source, op.new_attribute.source),
old_multitenancy: op.multitenancy,
multitenancy: op.old_multitenancy
})
end
end
defmodule DropForeignKey do
@moduledoc false
# We only run this migration in one direction, based on the input
# This is because the creation of a foreign key is handled by `references/3`
# We only need to drop it before altering an attribute with `references/3`
defstruct [:attribute, :schema, :table, :multitenancy, :direction, no_phase: true]
import Helper
def up(%{table: table, schema: schema, attribute: %{references: reference}, direction: :up}) do
"drop constraint(:#{as_atom(table)}, #{join([inspect(reference.name), option("prefix", schema)])})"
end
def up(_) do
""
end
def down(%{
table: table,
schema: schema,
attribute: %{references: reference},
direction: :down
}) do
"drop constraint(:#{as_atom(table)}, #{join([inspect(reference.name), option("prefix", schema)])})"
end
def down(_) do
""
end
end
defmodule RenameAttribute do
@moduledoc false
defstruct [
:old_attribute,
:new_attribute,
:table,
:schema,
:multitenancy,
:old_multitenancy,
no_phase: true
]
import Helper
def up(%{
old_attribute: old_attribute,
new_attribute: new_attribute,
schema: schema,
table: table
}) do
table_statement = join([":#{as_atom(table)}", option("prefix", schema)])
"rename table(#{table_statement}), #{inspect(old_attribute.source)}, to: #{inspect(new_attribute.source)}"
end
def down(
%{
old_attribute: old_attribute,
new_attribute: new_attribute
} = data
) do
up(%{data | new_attribute: old_attribute, old_attribute: new_attribute})
end
end
defmodule RemoveAttribute do
@moduledoc false
defstruct [:attribute, :schema, :table, :multitenancy, :old_multitenancy, commented?: true]
def up(%{attribute: attribute, commented?: true}) do
"""
# Attribute removal has been commented out to avoid data loss. See the migration generator documentation for more
# If you uncomment this, be sure to also uncomment the corresponding attribute *addition* in the `down` migration
# remove #{inspect(attribute.source)}
"""
end
def up(%{attribute: attribute}) do
"remove #{inspect(attribute.source)}"
end
def down(%{attribute: attribute, multitenancy: multitenancy, commented?: true}) do
prefix = """
# This is the `down` migration of the statement:
#
# remove #{inspect(attribute.source)}
#
"""
contents =
%AshPostgres.MigrationGenerator.Operation.AddAttribute{
attribute: attribute,
multitenancy: multitenancy
}
|> AshPostgres.MigrationGenerator.Operation.AddAttribute.up()
|> String.split("\n")
|> Enum.map_join("\n", &"# #{&1}")
prefix <> "\n" <> contents
end
def down(%{attribute: attribute, multitenancy: multitenancy, table: table, schema: schema}) do
AshPostgres.MigrationGenerator.Operation.AddAttribute.up(
%AshPostgres.MigrationGenerator.Operation.AddAttribute{
attribute: attribute,
table: table,
schema: schema,
multitenancy: multitenancy
}
)
end
end
defmodule AddUniqueIndex do
@moduledoc false
defstruct [
:identity,
:table,
:schema,
:multitenancy,
:old_multitenancy,
no_phase: true,
concurrently: false
]
import Helper
def up(%{
identity: %{
name: name,
keys: keys,
nils_distinct?: nils_distinct?,
where: where,
base_filter: base_filter,
index_name: index_name,
all_tenants?: all_tenants?
},
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys = index_keys(keys, all_tenants?, multitenancy)
index_name = index_name || "#{table}_#{name}_index"
cond do
base_filter && where ->
where = "(#{where}) AND (#{base_filter})"
"create unique_index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema), option("nulls_distinct", nils_distinct?), option("where", where)])})"
base_filter ->
base_filter = "(#{base_filter})"
"create unique_index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], where: \"#{base_filter}\", #{join(["name: \"#{index_name}\"", option("prefix", schema), option("nulls_distinct", nils_distinct?)])})"
where ->
where = "(#{where})"
"create unique_index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema), option("nulls_distinct", nils_distinct?), option("where", where)])})"
true ->
"create unique_index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema), option("nulls_distinct", nils_distinct?)])})"
end
end
def down(%{
identity: %{name: name, keys: keys, index_name: index_name, all_tenants?: all_tenants?},
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys = index_keys(keys, all_tenants?, multitenancy)
index_name = index_name || "#{table}_#{name}_index"
"drop_if_exists unique_index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema)])})"
end
end
defmodule AddCustomStatement do
@moduledoc false
defstruct [:statement, :table, no_phase: true]
def up(%{statement: %{up: up, code?: false}}) do
"""
execute(\"\"\"
#{String.trim(up)}
\"\"\")
"""
end
def up(%{statement: %{up: up, code?: true}}) do
up
end
def down(%{statement: %{down: down, code?: false}}) do
"""
execute(\"\"\"
#{String.trim(down)}
\"\"\")
"""
end
def down(%{statement: %{down: down, code?: true}}) do
down
end
end
defmodule RemoveCustomStatement do
@moduledoc false
defstruct [:statement, :table, no_phase: true]
def up(%{statement: statement, table: table}) do
AddCustomStatement.down(%AddCustomStatement{statement: statement, table: table})
end
def down(%{statement: statement, table: table}) do
AddCustomStatement.up(%AddCustomStatement{statement: statement, table: table})
end
end
defmodule AddCustomIndex do
@moduledoc false
defstruct [:table, :schema, :index, :base_filter, :multitenancy, no_phase: true]
import Helper
def up(%{
index: index,
table: table,
schema: schema,
base_filter: base_filter,
multitenancy: multitenancy
}) do
keys = index_keys(index.fields, index.all_tenants?, multitenancy)
index =
case {index.where, base_filter} do
{_where, nil} -> index
{nil, base_filter} -> %{index | where: base_filter}
{where, base_filter} -> %{index | where: base_filter <> " AND " <> where}
end
opts =
join([
option(:name, index.name),
option(:unique, index.unique),
option(:concurrently, index.concurrently),
option(:using, index.using),
option(:prefix, index.prefix),
option(:where, index.where),
option(:include, index.include),
option(:nulls_distinct, index.nulls_distinct),
option(:prefix, schema)
])
if opts == "" do
"create index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}])"
else
"create index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{opts})"
end
end
def down(%{schema: schema, index: index, table: table, multitenancy: multitenancy}) do
keys = index_keys(index.fields, index.all_tenants?, multitenancy)
opts =
join([
option(:name, index.name),
option(:prefix, schema)
])
if opts == "" do
"drop_if_exists index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}])"
else
"drop_if_exists index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{opts})"
end
end
end
defmodule AddReferenceIndex do
@moduledoc false
defstruct [:table, :schema, :source, :multitenancy, no_phase: true]
import Helper
def up(%{
source: source,
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys =
case multitenancy do
%{strategy: :attribute, attribute: attribute} when attribute != source ->
[attribute, source]
_ ->
[source]
end
opts =
join([
option(:prefix, schema)
])
if opts == "" do
"create index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}])"
else
"create index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{opts})"
end
end
def down(%{schema: schema, source: source, table: table, multitenancy: multitenancy}) do
keys =
case multitenancy do
%{strategy: :attribute, attribute: attribute} when attribute != source ->
[attribute, source]
_ ->
[source]
end
opts =
join([
option(:prefix, schema)
])
if opts == "" do
"drop_if_exists index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}])"
else
"drop_if_exists index(:#{as_atom(table)}, [#{Enum.map_join(keys, ", ", &inspect/1)}], #{opts})"
end
end
end
defmodule AddPrimaryKey do
@moduledoc false
defstruct [:schema, :table, :keys, no_phase: true]
def up(%{schema: schema, table: table, keys: keys, multitenancy: multitenancy}) do
keys = Enum.join(keys, ", ")
cond do
multitenancy.strategy == :context ->
"""
execute("ALTER TABLE \\\"\#{prefix()}\\\".\\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
schema ->
"""
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
true ->
"""
execute("ALTER TABLE \\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
end
end
def down(_) do
""
end
end
defmodule AddPrimaryKeyDown do
@moduledoc false
defstruct [:schema, :table, :keys, :remove_old?, no_phase: true]
def up(_) do
""
end
def down(%{
schema: schema,
table: table,
remove_old?: remove_old?,
keys: keys,
multitenancy: multitenancy
}) do
keys = Enum.join(keys, ", ")
cond do
multitenancy.strategy == :context ->
remove_old =
if remove_old? do
"""
execute("ALTER TABLE \\\"\#{prefix()}\\\".\\\"#{table}\\\" DROP constraint #{table}_pkey")
"""
end
"""
#{remove_old}
execute("ALTER TABLE \\\"\#{prefix()}\\\".\\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
not is_nil(schema) ->
remove_old =
if remove_old? do
"""
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" DROP constraint #{table}_pkey")
"""
end
"""
#{remove_old}
execute("ALTER TABLE \\\"#{schema}.#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
true ->
remove_old =
if remove_old? do
"""
execute("ALTER TABLE \\\"#{table}\\\" DROP constraint #{table}_pkey")
"""
end
"""
#{remove_old}
execute("ALTER TABLE \\\"#{table}\\\" ADD PRIMARY KEY (#{keys})")
"""
end
end
end
defmodule RemovePrimaryKey do
@moduledoc false
defstruct [:schema, :table, no_phase: true]
def up(%{schema: schema, table: table, old_multitenancy: multitenancy}) do
cond do
multitenancy.strategy == :context ->
"drop constraint(#{inspect(table)}, \"#{table}_pkey\", prefix: prefix())"
schema ->
"drop constraint(#{inspect(table)}, \"#{table}_pkey\", prefix: \"#{schema}\")"
true ->
"drop constraint(#{inspect(table)}, \"#{table}_pkey\")"
end
end
def down(_) do
""
end
end
defmodule RemovePrimaryKeyDown do
@moduledoc false
defstruct [:schema, :table, commented?: false, no_phase: true]
def up(_) do
""
end
def down(%{schema: schema, table: table, commented?: commented?, multitenancy: multitenancy}) do
comment =
if commented? do
"""
# Primary key removal is dropped because a corresponding attribute removal
# has been commented out. If you uncomment this, uncomment the attribute removal and vice versa.
"""
else
""
end
cond do
multitenancy.strategy == :context ->
"#{comment}drop constraint(#{inspect(table)}, \"#{table}_pkey\", prefix: prefix())"
schema ->
"#{comment}drop constraint(#{inspect(table)}, \"#{table}_pkey\", prefix: \"#{schema}\")"
true ->
"#{comment}drop constraint(#{inspect(table)}, \"#{table}_pkey\")"
end
end
end
defmodule RemoveCustomIndex do
@moduledoc false
defstruct [
:schema,
:table,
:index,
:base_filter,
:multitenancy,
:old_multitenancy,
no_phase: true
]
import Helper
def up(operation) do
AddCustomIndex.down(%{operation | multitenancy: operation.old_multitenancy})
end
def down(operation) do
AddCustomIndex.up(%{operation | multitenancy: operation.old_multitenancy})
end
end
defmodule RemoveReferenceIndex do
@moduledoc false
defstruct [:schema, :table, :source, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(operation) do
AddReferenceIndex.down(%{operation | multitenancy: operation.old_multitenancy})
end
def down(operation) do
AddReferenceIndex.up(%{operation | multitenancy: operation.old_multitenancy})
end
end
defmodule RenameUniqueIndex do
@moduledoc false
defstruct [
:new_identity,
:old_identity,
:table,
:schema,
:multitenancy,
:old_multitenancy,
no_phase: true
]
defp prefix_name(name, prefix) do
if prefix do
"#{prefix}.#{name}"
else
name
end
end
def up(%{
old_identity: %{index_name: old_index_name, name: old_name},
new_identity: %{index_name: new_index_name},
schema: schema,
table: table
}) do
old_index_name = old_index_name || "#{table}_#{old_name}_index"
"execute(\"ALTER INDEX #{prefix_name(old_index_name, schema)} " <>
"RENAME TO #{prefix_name(new_index_name, schema)}\")\n"
end
def down(%{
old_identity: %{index_name: old_index_name, name: old_name},
new_identity: %{index_name: new_index_name},
schema: schema,
table: table
}) do
old_index_name = old_index_name || "#{table}_#{old_name}_index"
"execute(\"ALTER INDEX #{prefix_name(new_index_name, schema)} " <>
"RENAME TO #{prefix_name(old_index_name, schema)}\")\n"
end
end
defmodule RemoveUniqueIndex do
@moduledoc false
defstruct [:identity, :schema, :table, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(operation) do
AddUniqueIndex.down(%{operation | multitenancy: operation.old_multitenancy})
end
def down(operation) do
AddUniqueIndex.up(%{operation | multitenancy: operation.old_multitenancy})
end
end
defmodule AddCheckConstraint do
@moduledoc false
defstruct [:table, :schema, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{
schema: schema,
constraint: %{
name: name,
check: check,
base_filter: base_filter
},
table: table
}) do
prefix = if schema, do: ", " <> option(:prefix, schema), else: ""
if base_filter do
~s'''
create constraint(:#{as_atom(table)}, :#{as_atom(name)}, check: """
(#{check}) OR NOT (#{base_filter})
"""#{prefix})
'''
else
~s'''
create constraint(:#{as_atom(table)}, :#{as_atom(name)}, check: """
#{check}
"""#{prefix})
'''
end
end
def down(%{
constraint: %{name: name},
schema: schema,
table: table
}) do
"drop_if_exists constraint(:#{as_atom(table)}, #{join([":#{as_atom(name)}", option(:prefix, schema)])})"
end
end
defmodule RemoveCheckConstraint do
@moduledoc false
defstruct [:table, :schema, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{constraint: %{name: name}, schema: schema, table: table}) do
"drop_if_exists constraint(:#{as_atom(table)}, #{join([":#{as_atom(name)}", option(:prefix, schema)])})"
end
def down(%{
constraint: %{
name: name,
check: check,
base_filter: base_filter
},
schema: schema,
table: table
}) do
prefix = if schema, do: ", " <> option(:prefix, schema), else: ""
if base_filter do
~s'''
create constraint(:#{as_atom(table)}, :#{as_atom(name)}, check: """
#{base_filter} AND #{check}
"""#{prefix})
'''
else
~s'''
create constraint(:#{as_atom(table)}, :#{as_atom(name)}, check: """
#{check}
"""#{prefix})
'''
end
end
end
end