improvement: add static schema specification in DSL

improvement: support static schema specification in migration generator
This commit is contained in:
Zach Daniel 2022-05-13 17:41:30 -04:00
parent 4e85466724
commit 6c5ee9aae5
7 changed files with 501 additions and 143 deletions

View file

@ -27,6 +27,7 @@ locals_without_parens = [
reference: 1,
reference: 2,
repo: 1,
schema: 1,
skip_unique_indexes: 1,
table: 1,
template: 1,

View file

@ -19,6 +19,11 @@ defmodule AshPostgres do
Extension.get_opt(resource, [:postgres], :table, nil, true)
end
@doc "The configured schema for a resource"
def schema(resource) do
Extension.get_opt(resource, [:postgres], :schema, nil, true)
end
@doc "The configured references for a resource"
def references(resource) do
Extension.get_entities(resource, [:postgres, :references])

View file

@ -290,8 +290,19 @@ defmodule AshPostgres.DataLayer do
],
table: [
type: :string,
doc:
"The table to store and read the resource from. Required unless `polymorphic?` is true."
doc: """
The table to store and read the resource from. Required unless `polymorphic?` is true.
If this is changed, the migration generator will not remove the old table.
"""
],
schema: [
type: :string,
doc: """
The schema that the table is located in.
Multitenancy supersedes this, so this acts as the schema in the cases that `global?: true` is set.
If this is changed, the migration generator will not remove the old table in the old schema.
"""
],
polymorphic?: [
type: :boolean,
@ -320,7 +331,7 @@ defmodule AshPostgres.DataLayer do
alias Ash.Filter
alias Ash.Query.{BooleanExpression, Not}
import AshPostgres, only: [repo: 1]
import AshPostgres, only: [repo: 1, schema: 1]
@behaviour Ash.DataLayer
@ -438,6 +449,13 @@ defmodule AshPostgres.DataLayer do
data_layer_query
end
data_layer_query =
if context[:data_layer][:schema] do
Ecto.Query.put_query_prefix(data_layer_query, to_string(context[:data_layer][:schema]))
else
data_layer_query
end
data_layer_query =
data_layer_query
|> default_bindings(resource, context)
@ -474,7 +492,11 @@ defmodule AshPostgres.DataLayer do
if Ash.Resource.Info.multitenancy_strategy(resource) == :context do
[prefix: tenant]
else
[]
if schema = schema(resource) do
[prefix: schema]
else
[]
end
end
|> add_timeout(changeset)
end
@ -491,7 +513,11 @@ defmodule AshPostgres.DataLayer do
if Ash.Resource.Info.multitenancy_strategy(resource) == :context do
[prefix: tenant]
else
[]
if schema = schema(resource) do
[prefix: schema]
else
[]
end
end
|> add_timeout(query)
end
@ -798,7 +824,7 @@ defmodule AshPostgres.DataLayer do
data_layer_query
| prefix:
to_string(
source_query.tenant || config[:default_prefix] ||
source_query.tenant || schema(resource) || config[:default_prefix] ||
"public"
)
}
@ -807,7 +833,7 @@ defmodule AshPostgres.DataLayer do
data_layer_query
| prefix:
to_string(
config[:default_prefix] ||
schema(resource) || config[:default_prefix] ||
"public"
)
}
@ -932,10 +958,19 @@ defmodule AshPostgres.DataLayer do
if AshPostgres.polymorphic?(record.__struct__) do
table = changeset.context[:data_layer][:table] || AshPostgres.table(record.__struct__)
if table do
Ecto.put_meta(record, source: table)
record =
if table do
Ecto.put_meta(record, source: table)
else
raise_table_error!(changeset.resource, operation)
end
prefix = changeset.context[:data_layer][:schema] || AshPostgres.schema(record.__struct__)
if prefix do
Ecto.put_meta(record, prefix: table)
else
raise_table_error!(changeset.resource, operation)
record
end
else
record

View file

@ -276,7 +276,7 @@ defmodule AshPostgres.MigrationGenerator do
defp deduplicate_snapshots(snapshots, opts, existing_snapshots \\ []) do
snapshots
|> Enum.group_by(fn snapshot ->
snapshot.table
{snapshot.table, snapshot.schema}
end)
|> Enum.map(fn {_table, [snapshot | _] = snapshots} ->
existing_snapshot =
@ -397,7 +397,8 @@ defmodule AshPostgres.MigrationGenerator do
on_delete: merge_uniq!(references, table, :on_delete, name),
on_update: merge_uniq!(references, table, :on_update, name),
name: merge_uniq!(references, table, :name, name),
table: merge_uniq!(references, table, :table, name)
table: merge_uniq!(references, table, :table, name),
schema: merge_uniq!(references, table, :schema, name)
}
end
end
@ -762,6 +763,7 @@ defmodule AshPostgres.MigrationGenerator do
attribute: %{
source: name
},
schema: schema,
table: table
} = add
| rest
@ -770,7 +772,7 @@ defmodule AshPostgres.MigrationGenerator do
) do
rest
|> Enum.take_while(fn op ->
op.table == table
op.table == table && op.schema == schema
end)
|> Enum.with_index()
|> Enum.find(fn
@ -808,40 +810,46 @@ defmodule AshPostgres.MigrationGenerator do
end
defp group_into_phases(
[%Operation.CreateTable{table: table, multitenancy: multitenancy} | rest],
[
%Operation.CreateTable{table: table, schema: schema, multitenancy: multitenancy} | rest
],
nil,
acc
) do
group_into_phases(rest, %Phase.Create{table: table, multitenancy: multitenancy}, acc)
group_into_phases(
rest,
%Phase.Create{table: table, schema: schema, multitenancy: multitenancy},
acc
)
end
defp group_into_phases(
[%Operation.AddAttribute{table: table} = op | rest],
%{table: table} = phase,
[%Operation.AddAttribute{table: table, schema: schema} = op | rest],
%{table: table, schema: schema} = phase,
acc
) do
group_into_phases(rest, %{phase | operations: [op | phase.operations]}, acc)
end
defp group_into_phases(
[%Operation.AlterAttribute{table: table} = op | rest],
%Phase.Alter{table: table} = phase,
[%Operation.AlterAttribute{table: table, schema: schema} = op | rest],
%Phase.Alter{table: table, schema: schema} = phase,
acc
) do
group_into_phases(rest, %{phase | operations: [op | phase.operations]}, acc)
end
defp group_into_phases(
[%Operation.RenameAttribute{table: table} = op | rest],
%Phase.Alter{table: table} = phase,
[%Operation.RenameAttribute{table: table, schema: schema} = op | rest],
%Phase.Alter{table: table, schema: schema} = phase,
acc
) do
group_into_phases(rest, %{phase | operations: [op | phase.operations]}, acc)
end
defp group_into_phases(
[%Operation.RemoveAttribute{table: table} = op | rest],
%{table: table} = phase,
[%Operation.RemoveAttribute{table: table, schema: schema} = op | rest],
%{table: table, schema: schema} = phase,
acc
) do
group_into_phases(rest, %{phase | operations: [op | phase.operations]}, acc)
@ -855,7 +863,8 @@ defmodule AshPostgres.MigrationGenerator do
phase = %Phase.Alter{
operations: [operation],
multitenancy: operation.multitenancy,
table: operation.table
table: operation.table,
schema: operation.schema
}
group_into_phases(rest, phase, acc)
@ -889,25 +898,27 @@ defmodule AshPostgres.MigrationGenerator do
end
defp after?(
%Operation.AddAttribute{attribute: %{order: l}, table: table},
%Operation.AddAttribute{attribute: %{order: r}, table: table}
%Operation.AddAttribute{attribute: %{order: l}, table: table, schema: schema},
%Operation.AddAttribute{attribute: %{order: r}, table: table, schema: schema}
),
do: l > r
defp after?(
%Operation.RenameUniqueIndex{
table: table
table: table,
schema: schema
},
%{table: table}
%{table: table, schema: schema}
) do
true
end
defp after?(
%Operation.AddUniqueIndex{
table: table
table: table,
schema: schema
},
%{table: table}
%{table: table, schema: schema}
) do
true
end
@ -916,9 +927,10 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table,
multitenancy: multitenancy
multitenancy: multitenancy,
schema: schema
},
%Operation.AddAttribute{table: table, attribute: %{source: source}}
%Operation.AddAttribute{table: table, attribute: %{source: source}, schema: schema}
) do
source in List.wrap(attribute_or_attributes) ||
(multitenancy.attribute && multitenancy.attribute in List.wrap(attribute_or_attributes))
@ -926,24 +938,30 @@ defmodule AshPostgres.MigrationGenerator do
defp after?(
%Operation.AddCustomIndex{
table: table
table: table,
schema: schema
},
%Operation.AddAttribute{table: table}
%Operation.AddAttribute{table: table, schema: schema}
) do
true
end
defp after?(%Operation.AddCheckConstraint{table: table}, %Operation.RemoveCheckConstraint{
table: table
}),
defp after?(
%Operation.AddCheckConstraint{table: table, schema: schema},
%Operation.RemoveCheckConstraint{
table: table,
schema: schema
}
),
do: true
defp after?(
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table
table: table,
schema: schema
},
%Operation.AlterAttribute{table: table, new_attribute: %{source: source}}
%Operation.AlterAttribute{table: table, new_attribute: %{source: source}, schema: schema}
) do
source in List.wrap(attribute_or_attributes)
end
@ -951,43 +969,61 @@ defmodule AshPostgres.MigrationGenerator do
defp after?(
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table
table: table,
schema: schema
},
%Operation.RenameAttribute{table: table, new_attribute: %{source: source}}
%Operation.RenameAttribute{
table: table,
new_attribute: %{source: source},
schema: schema
}
) do
source in List.wrap(attribute_or_attributes)
end
defp after?(
%Operation.RemoveUniqueIndex{table: table},
%Operation.AddUniqueIndex{table: table}
%Operation.RemoveUniqueIndex{table: table, schema: schema},
%Operation.AddUniqueIndex{table: table, schema: schema}
) do
false
end
defp after?(
%Operation.RemoveUniqueIndex{table: table},
%{table: table}
%Operation.RemoveUniqueIndex{table: table, schema: schema},
%{table: table, schema: schema}
) do
true
end
defp after?(
%Operation.RemoveCheckConstraint{constraint: %{attribute: attributes}, table: table},
%Operation.RemoveAttribute{table: table, attribute: %{source: source}}
%Operation.RemoveCheckConstraint{
constraint: %{attribute: attributes},
table: table,
schema: schema
},
%Operation.RemoveAttribute{table: table, attribute: %{source: source}, schema: schema}
) do
source in List.wrap(attributes)
end
defp after?(
%Operation.RemoveCheckConstraint{constraint: %{attribute: attributes}, table: table},
%Operation.RenameAttribute{table: table, old_attribute: %{source: source}}
%Operation.RemoveCheckConstraint{
constraint: %{attribute: attributes},
table: table,
schema: schema
},
%Operation.RenameAttribute{
table: table,
old_attribute: %{source: source},
schema: schema
}
) do
source in List.wrap(attributes)
end
defp after?(%Operation.AlterAttribute{table: table}, %Operation.DropForeignKey{
defp after?(%Operation.AlterAttribute{table: table, schema: schema}, %Operation.DropForeignKey{
table: table,
schema: schema,
direction: :up
}),
do: true
@ -995,56 +1031,68 @@ defmodule AshPostgres.MigrationGenerator do
defp after?(
%Operation.DropForeignKey{
table: table,
schema: schema,
direction: :down
},
%Operation.AlterAttribute{table: table}
%Operation.AlterAttribute{table: table, schema: schema}
),
do: true
defp after?(%Operation.AddAttribute{table: table}, %Operation.CreateTable{table: table}) do
defp after?(%Operation.AddAttribute{table: table, schema: schema}, %Operation.CreateTable{
table: table,
schema: schema
}) do
true
end
defp after?(
%Operation.AddAttribute{
attribute: %{
references: %{table: table, destination_field: name}
references: %{table: table, destination_field: name, schema: schema}
}
},
%Operation.AddAttribute{table: table, attribute: %{source: name}}
%Operation.AddAttribute{table: table, schema: schema, attribute: %{source: name}}
),
do: true
defp after?(
%Operation.AddAttribute{
table: table,
schema: schema,
attribute: %{
primary_key?: false
}
},
%Operation.AddAttribute{table: table, attribute: %{primary_key?: true}}
%Operation.AddAttribute{schema: schema, table: table, attribute: %{primary_key?: true}}
),
do: true
defp after?(
%Operation.AddAttribute{
table: table,
schema: schema,
attribute: %{
primary_key?: true
}
},
%Operation.RemoveAttribute{table: table, attribute: %{primary_key?: true}}
%Operation.RemoveAttribute{
schema: schema,
table: table,
attribute: %{primary_key?: true}
}
),
do: true
defp after?(
%Operation.AlterAttribute{
table: table,
schema: schema,
new_attribute: %{primary_key?: false},
old_attribute: %{primary_key?: true}
},
%Operation.AddAttribute{
table: table,
schema: schema,
attribute: %{
primary_key?: true
}
@ -1053,9 +1101,11 @@ defmodule AshPostgres.MigrationGenerator do
do: true
defp after?(
%Operation.RemoveAttribute{attribute: %{source: source}, table: table},
%Operation.RemoveAttribute{attribute: %{source: source}, table: table, schema: schema},
%Operation.AlterAttribute{
old_attribute: %{references: %{table: table, destination_field: source}}
old_attribute: %{
references: %{table: table, schema: schema, destination_field: source}
}
}
),
do: true
@ -1063,14 +1113,17 @@ defmodule AshPostgres.MigrationGenerator do
defp after?(
%Operation.AlterAttribute{
new_attribute: %{
references: %{table: table, destination_field: name}
references: %{table: table, schema: schema, destination_field: name}
}
},
%Operation.AddAttribute{table: table, attribute: %{source: name}}
%Operation.AddAttribute{schema: schema, table: table, attribute: %{source: name}}
),
do: true
defp after?(%Operation.AddCheckConstraint{table: table}, %Operation.CreateTable{table: table}) do
defp after?(%Operation.AddCheckConstraint{table: table, schema: schema}, %Operation.CreateTable{
table: table,
schema: schema
}) do
true
end
@ -1098,10 +1151,21 @@ defmodule AshPostgres.MigrationGenerator do
defp do_fetch_operations(snapshot, existing_snapshot, opts, acc \\ [])
defp do_fetch_operations(
%{schema: new_schema} = snapshot,
%{schema: old_schema},
opts,
[]
)
when new_schema != old_schema do
do_fetch_operations(snapshot, nil, opts, [])
end
defp do_fetch_operations(snapshot, nil, opts, acc) do
empty_snapshot = %{
attributes: [],
identities: [],
schema: nil,
custom_indexes: [],
check_constraints: [],
table: snapshot.table,
@ -1117,6 +1181,7 @@ defmodule AshPostgres.MigrationGenerator do
do_fetch_operations(snapshot, empty_snapshot, opts, [
%Operation.CreateTable{
table: snapshot.table,
schema: snapshot.schema,
multitenancy: snapshot.multitenancy,
old_multitenancy: empty_snapshot.multitenancy
}
@ -1138,9 +1203,10 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn custom_index ->
%Operation.AddCustomIndex{
index: custom_index,
table: old_snapshot.table,
multitenancy: old_snapshot.multitenancy,
base_filter: old_snapshot.base_filter
table: snapshot.table,
schema: snapshot.schema,
multitenancy: snapshot.multitenancy,
base_filter: snapshot.base_filter
}
end)
@ -1154,9 +1220,10 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn custom_index ->
%Operation.RemoveCustomIndex{
index: custom_index,
table: snapshot.table,
multitenancy: snapshot.multitenancy,
base_filter: snapshot.base_filter
table: old_snapshot.table,
schema: old_snapshot.schema,
multitenancy: old_snapshot.multitenancy,
base_filter: old_snapshot.base_filter
}
end)
@ -1173,7 +1240,11 @@ defmodule AshPostgres.MigrationGenerator do
end)
end
|> Enum.map(fn identity ->
%Operation.RemoveUniqueIndex{identity: identity, table: snapshot.table}
%Operation.RemoveUniqueIndex{
identity: identity,
table: snapshot.table,
schema: snapshot.schema
}
end)
unique_indexes_to_rename =
@ -1195,6 +1266,7 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.RenameUniqueIndex{
old_identity: old_identity,
new_identity: new_identity,
schema: snapshot.schema,
table: snapshot.table
}
end)
@ -1214,6 +1286,7 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn identity ->
%Operation.AddUniqueIndex{
identity: identity,
schema: snapshot.schema,
table: snapshot.table
}
end)
@ -1228,7 +1301,8 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn constraint ->
%Operation.AddCheckConstraint{
constraint: constraint,
table: snapshot.table
table: snapshot.table,
schema: snapshot.schema
}
end)
@ -1242,7 +1316,8 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.map(fn old_constraint ->
%Operation.RemoveCheckConstraint{
constraint: old_constraint,
table: old_snapshot.table
table: old_snapshot.table,
schema: old_snapshot.schema
}
end)
@ -1289,7 +1364,12 @@ defmodule AshPostgres.MigrationGenerator do
rename_attribute_events =
Enum.map(attributes_to_rename, fn {new, old} ->
%Operation.RenameAttribute{new_attribute: new, old_attribute: old, table: snapshot.table}
%Operation.RenameAttribute{
new_attribute: new,
old_attribute: old,
table: snapshot.table,
schema: snapshot.schema
}
end)
add_attribute_events =
@ -1298,16 +1378,19 @@ defmodule AshPostgres.MigrationGenerator do
[
%Operation.AddAttribute{
attribute: Map.delete(attribute, :references),
schema: snapshot.schema,
table: snapshot.table
},
%Operation.AlterAttribute{
old_attribute: Map.delete(attribute, :references),
new_attribute: attribute,
schema: snapshot.schema,
table: snapshot.table
},
%Operation.DropForeignKey{
attribute: attribute,
table: snapshot.table,
schema: snapshot.schema,
multitenancy: Map.get(attribute, :multitenancy),
direction: :down
}
@ -1316,7 +1399,8 @@ defmodule AshPostgres.MigrationGenerator do
[
%Operation.AddAttribute{
attribute: attribute,
table: snapshot.table
table: snapshot.table,
schema: snapshot.schema
}
]
end
@ -1330,12 +1414,14 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.DropForeignKey{
attribute: old_attribute,
table: snapshot.table,
schema: snapshot.schema,
multitenancy: old_snapshot.multitenancy,
direction: :up
},
%Operation.AlterAttribute{
new_attribute: new_attribute,
old_attribute: old_attribute,
schema: snapshot.schema,
table: snapshot.table
}
]
@ -1346,6 +1432,7 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.DropForeignKey{
attribute: new_attribute,
table: snapshot.table,
schema: snapshot.schema,
multitenancy: snapshot.multitenancy,
direction: :down
}
@ -1358,6 +1445,7 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.AlterAttribute{
new_attribute: Map.delete(new_attribute, :references),
old_attribute: Map.delete(old_attribute, :references),
schema: snapshot.schema,
table: snapshot.table
}
]
@ -1369,6 +1457,7 @@ defmodule AshPostgres.MigrationGenerator do
%Operation.RemoveAttribute{
attribute: attribute,
table: snapshot.table,
schema: snapshot.schema,
commented?: !opts.drop_columns
}
end)
@ -1572,7 +1661,10 @@ defmodule AshPostgres.MigrationGenerator do
|> Enum.uniq()
|> Enum.map(fn relationship ->
resource
|> do_snapshot(relationship.context[:data_layer][:table])
|> do_snapshot(
relationship.context[:data_layer][:table],
relationship.context[:data_layer][:schema]
)
|> Map.update!(:identities, fn identities ->
identity_index_names = AshPostgres.identity_index_names(resource)
@ -1603,6 +1695,7 @@ defmodule AshPostgres.MigrationGenerator do
destination_field_generated: source_attribute.generated?,
multitenancy: multitenancy(relationship.source),
table: AshPostgres.table(relationship.source),
schema: AshPostgres.table(relationship.source),
on_delete: AshPostgres.polymorphic_on_delete(relationship.source),
on_update: AshPostgres.polymorphic_on_update(relationship.source),
name:
@ -1620,11 +1713,12 @@ defmodule AshPostgres.MigrationGenerator do
end
end
defp do_snapshot(resource, table) do
defp do_snapshot(resource, table, schema \\ nil) do
snapshot = %{
attributes: attributes(resource, table),
identities: identities(resource),
table: table || AshPostgres.table(resource),
schema: schema || AshPostgres.schema(resource),
check_constraints: check_constraints(resource),
custom_indexes: custom_indexes(resource),
repo: AshPostgres.repo(resource),
@ -1776,6 +1870,10 @@ defmodule AshPostgres.MigrationGenerator do
on_delete: configured_reference.on_delete,
on_update: configured_reference.on_update,
name: configured_reference.name,
schema:
relationship.context[:data_layer][:schema] ||
AshPostgres.schema(relationship.destination) ||
AshPostgres.repo(relationship.destination).config()[:default_prefix],
table:
relationship.context[:data_layer][:table] ||
AshPostgres.table(relationship.destination)
@ -1792,6 +1890,10 @@ defmodule AshPostgres.MigrationGenerator do
|> Kernel.||(%{
on_delete: nil,
on_update: nil,
schema:
relationship.context[:data_layer][:schema] ||
AshPostgres.schema(relationship.destination) ||
AshPostgres.repo(relationship.destination).config()[:default_prefix],
name: nil
})
@ -1929,6 +2031,7 @@ defmodule AshPostgres.MigrationGenerator do
defp sanitize_snapshot(snapshot) do
snapshot
|> Map.put_new(:has_create_action, true)
|> Map.put_new(:schema, nil)
|> Map.update!(:identities, fn identities ->
Enum.map(identities, &load_identity(&1, snapshot.table))
end)
@ -2007,6 +2110,7 @@ defmodule AshPostgres.MigrationGenerator do
references ->
references
|> Map.update!(:destination_field, &String.to_atom/1)
|> Map.put_new(:schema, nil)
|> Map.put_new(:destination_field_default, "nil")
|> Map.put_new(:destination_field_generated, false)
|> Map.put_new(:on_delete, nil)

View file

@ -20,6 +20,12 @@ defmodule AshPostgres.MigrationGenerator.Operation do
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 option(key, value) do
if value do
"#{key}: #{inspect(value)}"
@ -60,12 +66,12 @@ defmodule AshPostgres.MigrationGenerator.Operation do
defmodule CreateTable do
@moduledoc false
defstruct [:table, :multitenancy, :old_multitenancy]
defstruct [:table, :schema, :multitenancy, :old_multitenancy]
end
defmodule AddAttribute do
@moduledoc false
defstruct [:attribute, :table, :multitenancy, :old_multitenancy]
defstruct [:attribute, :table, :schema, :multitenancy, :old_multitenancy]
import Helper
@ -77,6 +83,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
%{
table: table,
destination_field: destination_field,
schema: destination_schema,
multitenancy: %{strategy: :attribute, attribute: destination_attribute}
} = reference
} = attribute
@ -99,6 +106,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
with_match,
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
on_delete(reference),
on_update(reference),
size
@ -119,6 +127,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
%{
table: table,
destination_field: destination_field,
schema: destination_schema,
multitenancy: %{strategy: :attribute}
} = reference
} = attribute
@ -133,7 +142,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"references(:#{table}",
[
"column: #{inspect(destination_field)}",
"prefix: \"public\"",
option("prefix", destination_schema),
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
@ -212,11 +221,13 @@ defmodule AshPostgres.MigrationGenerator.Operation do
def up(%{
multitenancy: %{strategy: :context},
schema: schema,
attribute:
%{
references:
%{
table: table,
schema: destination_schema,
destination_field: destination_field
} = reference
} = attribute
@ -226,6 +237,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"size: #{attribute[:size]}"
end
destination_schema =
if schema != destination_schema do
destination_schema
end
[
"add #{inspect(attribute.source)}",
"references(:#{table}",
@ -233,7 +249,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"column: #{inspect(destination_field)}",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
"prefix: \"public\"",
option("prefix", destination_schema),
size,
on_delete(reference),
on_update(reference)
@ -248,8 +264,11 @@ defmodule AshPostgres.MigrationGenerator.Operation do
def up(%{
attribute:
%{references: %{table: table, destination_field: destination_field} = reference} =
attribute
%{
references:
%{table: table, schema: destination_schema, destination_field: destination_field} =
reference
} = attribute
}) do
size =
if attribute[:size] do
@ -263,6 +282,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"column: #{inspect(destination_field)}",
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
option("prefix", destination_schema),
size,
on_delete(reference),
on_update(reference)
@ -330,7 +350,14 @@ defmodule AshPostgres.MigrationGenerator.Operation do
defmodule AlterAttribute do
@moduledoc false
defstruct [:old_attribute, :new_attribute, :table, :multitenancy, :old_multitenancy]
defstruct [
:old_attribute,
:new_attribute,
:table,
:schema,
:multitenancy,
:old_multitenancy
]
import Helper
@ -360,12 +387,13 @@ defmodule AshPostgres.MigrationGenerator.Operation do
def up(%{
multitenancy: multitenancy,
old_attribute: old_attribute,
new_attribute: 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)
reference(multitenancy, attribute, schema)
else
inspect(attribute.type)
end
@ -382,7 +410,8 @@ defmodule AshPostgres.MigrationGenerator.Operation do
table: table,
destination_field: destination_field
} = reference
} = attribute
} = attribute,
_schema
) do
size =
if attribute[:size] do
@ -408,10 +437,17 @@ defmodule AshPostgres.MigrationGenerator.Operation do
%{
multitenancy: %{strategy: :attribute, attribute: destination_attribute},
table: table,
schema: destination_schema,
destination_field: destination_field
} = reference
} = attribute
} = attribute,
schema
) do
destination_schema =
if schema != destination_schema do
destination_schema
end
with_match =
if destination_attribute != destination_field do
"with: [#{source_attribute}: :#{destination_attribute}], match: :full"
@ -428,6 +464,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
option("prefix", destination_schema),
on_delete(reference),
on_update(reference),
")"
@ -440,17 +477,25 @@ defmodule AshPostgres.MigrationGenerator.Operation do
references:
%{
table: table,
destination_field: destination_field
destination_field: destination_field,
schema: destination_schema
} = reference
} = attribute
} = attribute,
schema
) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
end
destination_schema =
if schema != destination_schema do
destination_schema
end
join([
"references(:#{table}, column: #{inspect(destination_field)}, prefix: \"public\"",
"references(:#{table}, column: #{inspect(destination_field)}",
option("prefix", destination_schema),
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
@ -466,10 +511,17 @@ defmodule AshPostgres.MigrationGenerator.Operation do
references:
%{
table: table,
destination_field: destination_field
destination_field: destination_field,
schema: destination_schema
} = reference
} = attribute
} = attribute,
schema
) do
destination_schema =
if schema != destination_schema do
destination_schema
end
size =
if attribute[:size] do
"size: #{attribute[:size]}"
@ -477,6 +529,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
join([
"references(:#{table}, column: #{inspect(destination_field)}",
option("prefix", destination_schema),
"name: #{inspect(reference.name)}",
"type: #{inspect(reference_type(attribute, reference))}",
size,
@ -502,18 +555,25 @@ defmodule AshPostgres.MigrationGenerator.Operation do
# 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, :table, :multitenancy, :direction, no_phase: true]
defstruct [:attribute, :schema, :table, :multitenancy, :direction, no_phase: true]
def up(%{table: table, attribute: %{references: reference}, direction: :up}) do
"drop constraint(:#{table}, #{inspect(reference.name)})"
import Helper
def up(%{table: table, schema: schema, attribute: %{references: reference}, direction: :up}) do
"drop constraint(:#{table}, #{join([inspect(reference.name), option("prefix", schema)])})"
end
def up(_) do
""
end
def down(%{table: table, attribute: %{references: reference}, direction: :down}) do
"drop constraint(:#{table}, #{inspect(reference.name)})"
def down(%{
table: table,
schema: schema,
attribute: %{references: reference},
direction: :down
}) do
"drop constraint(:#{table}, #{join([inspect(reference.name), option("prefix", schema)])})"
end
def down(_) do
@ -527,23 +587,36 @@ defmodule AshPostgres.MigrationGenerator.Operation do
:old_attribute,
:new_attribute,
:table,
:schema,
:multitenancy,
:old_multitenancy,
no_phase: true
]
def up(%{old_attribute: old_attribute, new_attribute: new_attribute, table: table}) do
"rename table(:#{table}), #{inspect(old_attribute.source)}, to: #{inspect(new_attribute.source)}"
import Helper
def up(%{
old_attribute: old_attribute,
new_attribute: new_attribute,
schema: schema,
table: table
}) do
"rename table(:#{table}), #{inspect(old_attribute.source)}, to: #{join([inspect(new_attribute.source), option("prefix", schema)])}"
end
def down(%{new_attribute: old_attribute, old_attribute: new_attribute, table: table}) do
"rename table(:#{table}), #{inspect(old_attribute.source)}, to: #{inspect(new_attribute.source)}"
def down(
%{
new_attribute: old_attribute,
old_attribute: new_attribute
} = data
) do
up(%{data | new_attribute: old_attribute, old_attribute: new_attribute})
end
end
defmodule RemoveAttribute do
@moduledoc false
defstruct [:attribute, :table, :multitenancy, :old_multitenancy, commented?: true]
defstruct [:attribute, :schema, :table, :multitenancy, :old_multitenancy, commented?: true]
def up(%{attribute: attribute, commented?: true}) do
"""
@ -577,10 +650,12 @@ defmodule AshPostgres.MigrationGenerator.Operation do
prefix <> "\n" <> contents
end
def down(%{attribute: attribute, multitenancy: multitenancy}) do
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
}
)
@ -589,11 +664,14 @@ defmodule AshPostgres.MigrationGenerator.Operation do
defmodule AddUniqueIndex do
@moduledoc false
defstruct [:identity, :table, :multitenancy, :old_multitenancy, no_phase: true]
defstruct [:identity, :table, :schema, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{
identity: %{name: name, keys: keys, base_filter: base_filter, index_name: index_name},
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys =
@ -608,15 +686,16 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index_name = index_name || "#{table}_#{name}_index"
if base_filter do
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\", where: \"#{base_filter}\")"
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], where: \"#{base_filter}\", #{join(["name: \"#{index_name}\"", option("prefix", schema)])})"
else
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema)])})"
end
end
def down(%{
identity: %{name: name, keys: keys, index_name: index_name},
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys =
@ -630,18 +709,19 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index_name = index_name || "#{table}_#{name}_index"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option("prefix", schema)])})"
end
end
defmodule AddCustomIndex do
@moduledoc false
defstruct [:table, :index, :base_filter, :multitenancy, no_phase: true]
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
@ -669,7 +749,8 @@ defmodule AshPostgres.MigrationGenerator.Operation do
option(:using, index.using),
option(:prefix, index.prefix),
option(:where, index.where),
option(:include, index.include)
option(:include, index.include),
option(:prefix, schema)
])
if opts == "",
@ -677,7 +758,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do
else: "create index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{opts})"
end
def down(%{index: index, table: table, multitenancy: multitenancy}) do
def down(%{schema: schema, index: index, table: table, multitenancy: multitenancy}) do
index_name = AshPostgres.CustomIndex.name(table, index)
keys =
@ -689,16 +770,16 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index.fields
end
"drop_if_exists index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"drop_if_exists index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option(:prefix, schema)])})"
end
end
defmodule RemoveCustomIndex do
@moduledoc false
defstruct [:table, :index, :base_filter, :multitenancy, no_phase: true]
defstruct [:schema, :table, :index, :base_filter, :multitenancy, no_phase: true]
import Helper
def up(%{index: index, table: table, multitenancy: multitenancy}) do
def up(%{index: index, table: table, multitenancy: multitenancy, schema: schema}) do
index_name = AshPostgres.CustomIndex.name(table, index)
keys =
@ -710,12 +791,13 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index.fields
end
"drop_if_exists index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"drop_if_exists index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option(:prefix, schema)])})"
end
def down(%{
index: index,
table: table,
schema: schema,
base_filter: base_filter,
multitenancy: multitenancy
}) do
@ -743,7 +825,8 @@ defmodule AshPostgres.MigrationGenerator.Operation do
option(:using, index.using),
option(:prefix, index.prefix),
option(:where, index.where),
option(:include, index.include)
option(:include, index.include),
option(:prefix, schema)
])
"create index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{opts})"
@ -756,41 +839,55 @@ defmodule AshPostgres.MigrationGenerator.Operation do
: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 #{old_index_name} " <>
"RENAME TO #{new_index_name}\")\n"
"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 #{new_index_name} " <>
"RENAME TO #{old_index_name}\")\n"
"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, :table, :multitenancy, :old_multitenancy, no_phase: true]
defstruct [:identity, :schema, :table, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{
identity: %{name: name, keys: keys, index_name: index_name},
table: table,
schema: schema,
old_multitenancy: multitenancy
}) do
keys =
@ -804,12 +901,13 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index_name = index_name || "#{table}_#{name}_index"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"drop_if_exists unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option(:prefix, schema)])})"
end
def down(%{
identity: %{name: name, keys: keys, base_filter: base_filter, index_name: index_name},
table: table,
schema: schema,
multitenancy: multitenancy
}) do
keys =
@ -824,18 +922,21 @@ defmodule AshPostgres.MigrationGenerator.Operation do
index_name = index_name || "#{table}_#{name}_index"
if base_filter do
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\", where: \"#{base_filter}\")"
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], where: \"#{base_filter}\", #{join(["name: \"#{index_name}\"", option(:prefix, schema)])})"
else
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], name: \"#{index_name}\")"
"create unique_index(:#{table}, [#{Enum.map_join(keys, ",", &inspect/1)}], #{join(["name: \"#{index_name}\"", option(:prefix, schema)])})"
end
end
end
defmodule AddCheckConstraint do
@moduledoc false
defstruct [:table, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
defstruct [:table, :schema, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{
schema: schema,
constraint: %{
name: name,
check: check,
@ -844,26 +945,29 @@ defmodule AshPostgres.MigrationGenerator.Operation do
table: table
}) do
if base_filter do
"create constraint(:#{table}, :#{name}, check: \"#{base_filter} AND #{check}\")"
"create constraint(:#{table}, :#{name}, #{join(["check: \"#{base_filter} AND #{check}\")", option(:prefix, schema)])}"
else
"create constraint(:#{table}, :#{name}, check: \"#{check}\")"
"create constraint(:#{table}, :#{name}, #{join(["check: \"#{check}\")", option(:prefix, schema)])}"
end
end
def down(%{
constraint: %{name: name},
schema: schema,
table: table
}) do
"drop_if_exists constraint(:#{table}, :#{name})"
"drop_if_exists constraint(:#{table}, #{join([":#{name}", option(:prefix, schema)])})"
end
end
defmodule RemoveCheckConstraint do
@moduledoc false
defstruct [:table, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
defstruct [:table, :schema, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
def up(%{constraint: %{name: name}, table: table}) do
"drop_if_exists constraint(:#{table}, :#{name})"
import Helper
def up(%{constraint: %{name: name}, schema: schema, table: table}) do
"drop_if_exists constraint(:#{table}, #{join([":#{name}", option(:prefix, schema)])})"
end
def down(%{
@ -872,12 +976,13 @@ defmodule AshPostgres.MigrationGenerator.Operation do
check: check,
base_filter: base_filter
},
schema: schema,
table: table
}) do
if base_filter do
"create constraint(:#{table}, :#{name}, check: \"#{base_filter} AND #{check}\")"
"create constraint(:#{table}, :#{name}, #{join(["check: \"#{base_filter} AND #{check}\")", option(:prefix, schema)])}"
else
"create constraint(:#{table}, :#{name}, check: \"#{check}\")"
"create constraint(:#{table}, :#{name}, #{join(["check: \"#{check}\")", option(:prefix, schema)])}"
end
end
end

View file

@ -3,34 +3,48 @@ defmodule AshPostgres.MigrationGenerator.Phase do
defmodule Create do
@moduledoc false
defstruct [:table, :multitenancy, operations: [], commented?: false]
defstruct [:table, :schema, :multitenancy, operations: [], commented?: false]
def up(%{table: table, operations: operations, multitenancy: multitenancy}) do
def up(%{schema: schema, table: table, operations: operations, multitenancy: multitenancy}) do
if multitenancy.strategy == :context do
"create table(:#{table}, primary_key: false, prefix: prefix()) do\n" <>
Enum.map_join(operations, "\n", fn operation -> operation.__struct__.up(operation) end) <>
"\nend"
else
"create table(:#{table}, primary_key: false) do\n" <>
opts =
if schema do
", prefix: \"#{schema}\""
else
""
end
"create table(:#{table}, primary_key: false#{opts}) do\n" <>
Enum.map_join(operations, "\n", fn operation -> operation.__struct__.up(operation) end) <>
"\nend"
end
end
def down(%{table: table, multitenancy: multitenancy}) do
def down(%{schema: schema, table: table, multitenancy: multitenancy}) do
if multitenancy.strategy == :context do
"drop table(:#{inspect(table)}, prefix: prefix())"
else
"drop table(:#{inspect(table)})"
opts =
if schema do
", prefix: \"#{schema}\""
else
""
end
"drop table(:#{inspect(table)}#{opts})"
end
end
end
defmodule Alter do
@moduledoc false
defstruct [:table, :multitenancy, operations: [], commented?: false]
defstruct [:schema, :table, :multitenancy, operations: [], commented?: false]
def up(%{table: table, operations: operations, multitenancy: multitenancy}) do
def up(%{table: table, schema: schema, operations: operations, multitenancy: multitenancy}) do
body =
Enum.map_join(operations, "\n", fn operation -> operation.__struct__.up(operation) end)
@ -39,13 +53,20 @@ defmodule AshPostgres.MigrationGenerator.Phase do
body <>
"\nend"
else
"alter table(:#{table}) do\n" <>
opts =
if schema do
", prefix: \"#{schema}\""
else
""
end
"alter table(:#{table}#{opts}) do\n" <>
body <>
"\nend"
end
end
def down(%{table: table, operations: operations, multitenancy: multitenancy}) do
def down(%{table: table, schema: schema, operations: operations, multitenancy: multitenancy}) do
body =
operations
|> Enum.reverse()
@ -56,7 +77,14 @@ defmodule AshPostgres.MigrationGenerator.Phase do
body <>
"\nend"
else
"alter table(:#{table}) do\n" <>
opts =
if schema do
", prefix: \"#{schema}\""
else
""
end
"alter table(:#{table}#{opts}) do\n" <>
body <>
"\nend"
end

View file

@ -128,6 +128,86 @@ defmodule AshPostgres.MigrationGeneratorTest do
end
end
describe "creating initial snapshots for resources with a schema" do
setup do
on_exit(fn ->
File.rm_rf!("test_snapshots_path")
File.rm_rf!("test_migration_path")
end)
defposts do
postgres do
migration_types second_title: {:varchar, 16}
schema("example")
end
identities do
identity(:title, [:title])
end
attributes do
uuid_primary_key(:id)
attribute(:title, :string)
attribute(:second_title, :string)
end
end
defapi([Post])
Mix.shell(Mix.Shell.Process)
{:ok, _} =
Ecto.Adapters.SQL.query(
AshPostgres.TestRepo,
"""
CREATE SCHEMA IF NOT EXISTS example;
"""
)
AshPostgres.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
:ok
end
test "the migration sets up resources correctly" do
# the snapshot exists and contains valid json
assert File.read!(Path.wildcard("test_snapshots_path/test_repo/posts/*.json"))
|> Jason.decode!(keys: :atoms!)
assert [file] = Path.wildcard("test_migration_path/**/*_migrate_resources*.exs")
file_contents = File.read!(file)
# the migration creates the table
assert file_contents =~ "create table(:posts, primary_key: false, prefix: \"example\") do"
# the migration sets up the custom_indexes
assert file_contents =~
~S{create index(:posts, ["id"], name: "test_unique_index", unique: true, prefix: "example")}
assert file_contents =~ ~S{create index(:posts, ["id"]}
# the migration adds the id, with its default
assert file_contents =~
~S[add :id, :uuid, null: false, default: fragment("uuid_generate_v4()"), primary_key: true]
# the migration adds other attributes
assert file_contents =~ ~S[add :title, :text]
# the migration adds custom attributes
assert file_contents =~ ~S[add :second_title, :varchar, size: 16]
# the migration creates unique_indexes based on the identities of the resource
assert file_contents =~
~S{create unique_index(:posts, [:title], name: "posts_title_index", prefix: "example")}
end
end
describe "creating follow up migrations" do
setup do
on_exit(fn ->
@ -527,7 +607,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
assert [file] = Path.wildcard("test_migration_path/**/*_migrate_resources*.exs")
assert File.read!(file) =~
~S[references(:posts, column: :id, name: "posts_post_id_fkey", type: :uuid)]
~S[references(:posts, column: :id, name: "posts_post_id_fkey", type: :uuid, prefix: "public")]
end
test "when modified, the foreign key is dropped before modification" do
@ -590,7 +670,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
|> Enum.at(1)
assert File.read!(file) =~
~S[references(:posts, column: :id, name: "special_post_fkey", type: :uuid, on_delete: :delete_all, on_update: :update_all)]
~S[references(:posts, column: :id, prefix: "public", name: "special_post_fkey", type: :uuid, on_delete: :delete_all, on_update: :update_all)]
assert File.read!(file) =~ ~S[drop constraint(:posts, "posts_post_id_fkey")]
end
@ -757,7 +837,7 @@ defmodule AshPostgres.MigrationGeneratorTest do
assert [file] = Path.wildcard("test_migration_path/**/*_migrate_resources*.exs")
assert File.read!(file) =~
~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid)]
~S[references(:post_comments, column: :id, name: "posts_best_comment_id_fkey", type: :uuid, prefix: "public")]
end
end