improvement: get at least one test passing

This commit is contained in:
Zach Daniel 2023-09-23 01:32:12 -04:00
parent c8c811c7eb
commit 6176f80e9b
41 changed files with 1464 additions and 1080 deletions

View file

@ -1,8 +1,6 @@
spark_locals_without_parens = [
base_filter_sql: 1,
check: 1,
check_constraint: 2,
check_constraint: 3,
code?: 1,
concurrently: 1,
create?: 1,

3
.gitignore vendored
View file

@ -25,3 +25,6 @@ ash_sqlite-*.tar
test_migration_path
test_snapshots_path
test/test.db
test/test.db-shm
test/test.db-wal

View file

@ -21,29 +21,14 @@ if Mix.env() == :test do
config :ash, :validate_api_config_inclusion?, false
config :ash_sqlite, AshSqlite.TestRepo,
username: "sqlite",
database: "ash_sqlite",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
# sobelow_skip ["Config.Secrets"]
config :ash_sqlite, AshSqlite.TestRepo, password: "sqlite"
config :ash_sqlite, AshSqlite.TestRepo, migration_primary_key: [name: :id, type: :binary_id]
config :ash_sqlite, AshSqlite.TestNoSandboxRepo,
username: "sqlite",
database: "ash_sqlite_test",
hostname: "localhost"
# sobelow_skip ["Config.Secrets"]
config :ash_sqlite, AshSqlite.TestNoSandboxRepo, password: "sqlite"
config :ash_sqlite, AshSqlite.TestNoSandboxRepo,
database: Path.join(__DIR__, "../test/test.db"),
pool_size: 1,
migration_lock: false,
pool: Ecto.Adapters.SQL.Sandbox,
migration_primary_key: [name: :id, type: :binary_id]
config :ash_sqlite,
ecto_repos: [AshSqlite.TestRepo, AshSqlite.TestNoSandboxRepo],
ecto_repos: [AshSqlite.TestRepo],
ash_apis: [
AshSqlite.Test.Api
]

View file

@ -1,30 +0,0 @@
defmodule AshSqlite.CheckConstraint do
@moduledoc "Represents a configured check constraint on the table backing a resource"
defstruct [:attribute, :name, :message, :check]
def schema do
[
attribute: [
type: :any,
doc:
"The attribute or list of attributes to which an error will be added if the check constraint fails",
required: true
],
name: [
type: :string,
required: true,
doc: "The name of the constraint"
],
message: [
type: :string,
doc: "The message to be added if the check constraint fails"
],
check: [
type: :string,
doc:
"The contents of the check. If this is set, the migration generator will include it when generating migrations"
]
]
end
end

View file

@ -5,7 +5,6 @@ defmodule AshSqlite.CustomIndex do
:fields,
:name,
:unique,
:concurrently,
:using,
:where,
:include,
@ -30,11 +29,6 @@ defmodule AshSqlite.CustomIndex do
doc: "indicates whether the index should be unique.",
default: false
],
concurrently: [
type: :boolean,
doc: "indicates whether the index should be created/dropped concurrently.",
default: false
],
using: [
type: :string,
doc: "configures the index type."

View file

@ -136,44 +136,6 @@ defmodule AshSqlite.DataLayer do
]
}
@check_constraint %Spark.Dsl.Entity{
name: :check_constraint,
describe: """
Add a check constraint to be validated.
If a check constraint exists on the table but not in this section, and it produces an error, a runtime error will be raised.
Provide a list of attributes instead of a single attribute to add the message to multiple attributes.
By adding the `check` option, the migration generator will include it when generating migrations.
""",
examples: [
"""
check_constraint :price, "price_must_be_positive", check: "price > 0", message: "price must be positive"
"""
],
args: [:attribute, :name],
target: AshSqlite.CheckConstraint,
schema: AshSqlite.CheckConstraint.schema()
}
@check_constraints %Spark.Dsl.Section{
name: :check_constraints,
describe: """
A section for configuring the check constraints for a given table.
This can be used to automatically create those check constraints, or just to provide message when they are raised
""",
examples: [
"""
check_constraints do
check_constraint :price, "price_must_be_positive", check: "price > 0", message: "price must be positive"
end
"""
],
entities: [@check_constraint]
}
@references %Spark.Dsl.Section{
name: :references,
describe: """
@ -217,8 +179,7 @@ defmodule AshSqlite.DataLayer do
sections: [
@custom_indexes,
@custom_statements,
@references,
@check_constraints
@references
],
modules: [
:repo
@ -364,20 +325,9 @@ defmodule AshSqlite.DataLayer do
import Ecto.Query, only: [from: 2, subquery: 1]
@impl true
def can?(_, :async_engine), do: true
def can?(_, :async_engine), do: false
def can?(_, :bulk_create), do: true
def can?(_, {:lock, :for_update}), do: true
def can?(_, {:lock, string}) do
string = String.trim_trailing(string, " NOWAIT")
String.upcase(string) in [
"FOR UPDATE",
"FOR NO KEY UPDATE",
"FOR SHARE",
"FOR KEY SHARE"
]
end
def can?(_, {:lock, _}), do: false
def can?(_, :transact), do: true
def can?(_, :composite_primary_key), do: true
@ -470,7 +420,7 @@ defmodule AshSqlite.DataLayer do
data_layer_query
end
default_bindings(data_layer_query, resource, context)
{:ok, default_bindings(data_layer_query, resource, context)}
end
@impl true
@ -545,8 +495,8 @@ defmodule AshSqlite.DataLayer do
defp no_table?(_), do: false
defp repo_opts(timeout, nil, _resource) do
[]
|> add_timeout(timeout)
[]
|> add_timeout(timeout)
end
defp repo_opts(timeout, _resource) do
@ -563,7 +513,7 @@ defmodule AshSqlite.DataLayer do
def functions(_resource) do
[
AshSqlite.Functions.Fragment,
AshSqlite.Functions.Like,
AshSqlite.Functions.Like
]
end
@ -623,7 +573,6 @@ defmodule AshSqlite.DataLayer do
else
{:ok,
Stream.zip_with(results, changesets, fn result, changeset ->
Ash.Resource.put_metadata(
result,
:bulk_create_index,
@ -717,7 +666,6 @@ defmodule AshSqlite.DataLayer do
|> Map.update!(:filters, &Map.merge(&1, filters))
|> add_configured_foreign_key_constraints(record.__struct__)
|> add_unique_indexes(record.__struct__, changeset)
|> add_check_constraints(record.__struct__)
|> add_exclusion_constraints(record.__struct__)
case type do
@ -769,17 +717,16 @@ defmodule AshSqlite.DataLayer do
changeset.context[:data_layer][:table] ||
AshSqlite.DataLayer.Info.table(record.__struct__)
if table do
Ecto.put_meta(record, source: table)
if table do
Ecto.put_meta(record, source: table)
else
if table_error? do
raise_table_error!(changeset.resource, operation)
else
if table_error? do
raise_table_error!(changeset.resource, operation)
else
record
end
record
end
else
end
else
record
end
end
@ -847,21 +794,6 @@ defmodule AshSqlite.DataLayer do
def to_ecto(other), do: other
defp add_check_constraints(changeset, resource) do
resource
|> AshSqlite.DataLayer.Info.check_constraints()
|> Enum.reduce(changeset, fn constraint, changeset ->
constraint.attribute
|> List.wrap()
|> Enum.reduce(changeset, fn attribute, changeset ->
Ecto.Changeset.check_constraint(changeset, attribute,
name: constraint.name,
message: constraint.message || "is invalid"
)
end)
end)
end
defp add_exclusion_constraints(changeset, resource) do
resource
|> AshSqlite.DataLayer.Info.exclusion_constraint_names()
@ -999,34 +931,34 @@ defmodule AshSqlite.DataLayer do
@impl true
def upsert(resource, changeset, keys \\ nil) do
keys = keys || Ash.Resource.Info.primary_key(resource)
keys = keys || Ash.Resource.Info.primary_key(resource)
explicitly_changing_attributes =
Enum.map(
Map.keys(changeset.attributes) -- Map.get(changeset, :defaults, []) -- keys,
fn key ->
{key, Ash.Changeset.get_attribute(changeset, key)}
end
)
explicitly_changing_attributes =
Enum.map(
Map.keys(changeset.attributes) -- Map.get(changeset, :defaults, []) -- keys,
fn key ->
{key, Ash.Changeset.get_attribute(changeset, key)}
end
)
on_conflict =
changeset
|> update_defaults()
|> Keyword.merge(explicitly_changing_attributes)
on_conflict =
changeset
|> update_defaults()
|> Keyword.merge(explicitly_changing_attributes)
case bulk_create(resource, [changeset], %{
single?: true,
upsert?: true,
upsert_keys: keys,
upsert_fields: Keyword.keys(on_conflict),
return_records?: true
}) do
{:ok, [result]} ->
{:ok, result}
case bulk_create(resource, [changeset], %{
single?: true,
upsert?: true,
upsert_keys: keys,
upsert_fields: Keyword.keys(on_conflict),
return_records?: true
}) do
{:ok, [result]} ->
{:ok, result}
{:error, error} ->
{:error, error}
end
{:error, error} ->
{:error, error}
end
end
defp conflict_target(resource, keys) do
@ -1264,41 +1196,6 @@ defmodule AshSqlite.DataLayer do
end
end
@impl true
def lock(query, :for_update, _) do
if query.distinct do
new_query =
Ecto.Query.lock(%{query | distinct: nil}, [{^0, a}], fragment("FOR UPDATE OF ?", a))
q = from(row in subquery(new_query), [])
{:ok, %{q | distinct: query.distinct}}
else
{:ok, Ecto.Query.lock(query, [{^0, a}], fragment("FOR UPDATE OF ?", a))}
end
end
@locks [
"FOR UPDATE",
"FOR NO KEY UPDATE",
"FOR SHARE",
"FOR KEY SHARE"
]
for lock <- @locks do
frag = "#{lock} OF ?"
def lock(query, unquote(lock), _) do
{:ok, Ecto.Query.lock(query, [{^0, a}], fragment(unquote(frag), a))}
end
frag = "#{lock} OF ? NOWAIT"
lock = "#{lock} NOWAIT"
def lock(query, unquote(lock), _) do
{:ok, Ecto.Query.lock(query, [{^0, a}], fragment(unquote(frag), a))}
end
end
@impl true
def sort(query, sort, _resource) do
{:ok, Map.update!(query, :__ash_bindings__, &Map.put(&1, :sort, sort))}
@ -1374,7 +1271,6 @@ defmodule AshSqlite.DataLayer do
:with_ctes,
:limit,
:offset,
:lock,
:preload,
:update,
:where

View file

@ -13,7 +13,6 @@ defmodule AshSqlite.DataLayer.Info do
Extension.get_opt(resource, [:sqlite], :table, nil, true)
end
@doc "The configured references for a resource"
def references(resource) do
Extension.get_entities(resource, [:sqlite, :references])
@ -41,10 +40,6 @@ defmodule AshSqlite.DataLayer.Info do
Extension.get_opt(resource, [:sqlite], :migration_ignore_attributes, [])
end
@doc "The configured check_constraints for a resource"
def check_constraints(resource) do
Extension.get_entities(resource, [:sqlite, :check_constraints])
end
@doc "The configured custom_indexes for a resource"
def custom_indexes(resource) do
@ -120,5 +115,4 @@ defmodule AshSqlite.DataLayer.Info do
def skip_unique_indexes(resource) do
Extension.get_opt(resource, [:sqlite], :skip_unique_indexes, [])
end
end

View file

@ -385,24 +385,7 @@ defmodule AshSqlite.Expr do
type
) do
if options[:trim?] do
require_ash_functions!(query, "string_split(..., trim?: true)")
do_dynamic_expr(
query,
%Fragment{
embedded?: pred_embedded?,
arguments: [
raw: "ash_trim_whitespace(string_to_array(",
expr: string,
raw: ", NULLIF(",
expr: delimiter,
raw: ", '')))"
]
},
bindings,
embedded?,
type
)
raise "trim?: true not supported by AshSqlite"
else
do_dynamic_expr(
query,
@ -641,44 +624,10 @@ defmodule AshSqlite.Expr do
)
:|| ->
require_ash_functions!(query, "||")
do_dynamic_expr(
query,
%Fragment{
embedded?: pred_embedded?,
arguments: [
raw: "ash_elixir_or(",
casted_expr: left_expr,
raw: ", ",
casted_expr: right_expr,
raw: ")"
]
},
bindings,
embedded?,
type
)
raise "|| operator not supported by AshSqlite"
:&& ->
require_ash_functions!(query, "&&")
do_dynamic_expr(
query,
%Fragment{
embedded?: pred_embedded?,
arguments: [
raw: "ash_elixir_and(",
casted_expr: left_expr,
raw: ", ",
casted_expr: right_expr,
raw: ")"
]
},
bindings,
embedded?,
type
)
raise "&& operator not supported by AshSqlite"
other ->
raise "Operator not implemented #{other}"
@ -1289,19 +1238,6 @@ defmodule AshSqlite.Expr do
end
end
defp require_ash_functions!(query, operator) do
installed_extensions =
AshSqlite.DataLayer.Info.repo(query.__ash_bindings__.resource).installed_extensions()
unless "ash-functions" in installed_extensions do
raise """
Cannot use `#{operator}` without adding the extension `ash-functions` to your repo.
Add it to the list in `installed_extensions/0` and generate migrations.
"""
end
end
defp require_extension!(query, extension, context) do
repo = AshSqlite.DataLayer.Info.repo(query.__ash_bindings__.resource)

View file

@ -579,6 +579,7 @@ defmodule AshSqlite.Join do
relationship_destination =
relationship_destination
|> Ecto.Queryable.to_query()
binding_kinds =
case kind do
:left ->

View file

@ -25,7 +25,7 @@ defmodule AshSqlite.MigrationGenerator do
all_resources = Enum.uniq(Enum.flat_map(apis, &Ash.Api.Info.resources/1))
snapshots =
snapshots =
all_resources
|> Enum.filter(fn resource ->
Ash.DataLayer.data_layer(resource) == AshSqlite.DataLayer &&
@ -142,8 +142,6 @@ defmodule AshSqlite.MigrationGenerator do
Path.join([Mix.Project.deps_paths()[app] || File.cwd!(), "priv", "resource_snapshots"])
end
@latest_ash_functions_version 1
defp create_extension_migrations(repos, opts) do
for repo <- repos do
snapshot_path = snapshot_path(opts, repo)
@ -158,7 +156,7 @@ defmodule AshSqlite.MigrationGenerator do
[]
end
{extensions_snapshot, installed_extensions} =
{_extensions_snapshot, installed_extensions} =
case installed_extensions do
installed when is_list(installed) ->
{%{
@ -186,15 +184,6 @@ defmodule AshSqlite.MigrationGenerator do
|> Enum.filter(fn {name, _extension} -> !Enum.member?(installed_extensions, name) end)
|> Enum.map(fn {_name, extension} -> extension end)
to_install =
if "ash-functions" in requesteds &&
extensions_snapshot[:ash_functions_version] !=
@latest_ash_functions_version do
Enum.uniq(["ash-functions" | to_install])
else
to_install
end
if Enum.empty?(to_install) do
Mix.shell().info("No extensions to install")
:ok
@ -227,26 +216,20 @@ defmodule AshSqlite.MigrationGenerator do
install =
Enum.map_join(to_install, "\n", fn
"ash-functions" ->
install_ash_functions(extensions_snapshot[:ash_functions_version])
{_ext_name, version, up_fn, _down_fn} when is_function(up_fn, 1) ->
up_fn.(version)
extension ->
"execute(\"CREATE EXTENSION IF NOT EXISTS \\\"#{extension}\\\"\")"
raise "only custom extensions supported currently. Got #{inspect(extension)}"
end)
uninstall =
Enum.map_join(to_install, "\n", fn
"ash-functions" ->
"execute(\"DROP FUNCTION IF EXISTS ash_elixir_and(BOOLEAN, ANYCOMPATIBLE), ash_elixir_and(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(ANYCOMPATIBLE, ANYCOMPATIBLE), ash_elixir_or(BOOLEAN, ANYCOMPATIBLE)\")"
{_ext_name, version, _up_fn, down_fn} when is_function(down_fn, 1) ->
down_fn.(version)
extension ->
"# execute(\"DROP EXTENSION IF EXISTS \\\"#{extension}\\\"\")"
raise "only custom extensions supported currently. Got #{inspect(extension)}"
end)
contents = """
@ -277,8 +260,7 @@ defmodule AshSqlite.MigrationGenerator do
Jason.encode!(
%{
installed: installed
}
|> set_ash_functions(installed),
},
pretty: true
)
@ -289,122 +271,6 @@ defmodule AshSqlite.MigrationGenerator do
end
end
defp install_ash_functions(nil) do
"""
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_elixir_or(left BOOLEAN, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE)
AS $$ SELECT COALESCE(NULLIF($1, FALSE), $2) $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_elixir_or(left ANYCOMPATIBLE, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE)
AS $$ SELECT COALESCE($1, $2) $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_elixir_and(left BOOLEAN, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) AS $$
SELECT CASE
WHEN $1 IS TRUE THEN $2
ELSE $1
END $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_elixir_and(left ANYCOMPATIBLE, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) AS $$
SELECT CASE
WHEN $1 IS NOT NULL THEN $2
ELSE $1
END $$
LANGUAGE SQL
IMMUTABLE;
\"\"\")
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_trim_whitespace(arr text[])
RETURNS text[] AS $$
DECLARE
start_index INT = 1;
end_index INT = array_length(arr, 1);
BEGIN
WHILE start_index <= end_index AND arr[start_index] = '' LOOP
start_index := start_index + 1;
END LOOP;
WHILE end_index >= start_index AND arr[end_index] = '' LOOP
end_index := end_index - 1;
END LOOP;
IF start_index > end_index THEN
RETURN ARRAY[]::text[];
ELSE
RETURN arr[start_index : end_index];
END IF;
END; $$
LANGUAGE plpgsql
IMMUTABLE;
\"\"\")
"""
end
defp install_ash_functions(0) do
"""
execute(\"\"\"
ALTER FUNCTION ash_elixir_or(left BOOLEAN, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) IMMUTABLE
\"\"\")
execute(\"\"\"
ALTER FUNCTION ash_elixir_or(left ANYCOMPATIBLE, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) IMMUTABLE
\"\"\")
execute(\"\"\"
ALTER FUNCTION ash_elixir_and(left BOOLEAN, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) IMMUTABLE
\"\"\")
execute(\"\"\"
ALTER FUNCTION ash_elixir_and(left ANYCOMPATIBLE, in right ANYCOMPATIBLE, out f1 ANYCOMPATIBLE) IMMUTABLE
\"\"\")
execute(\"\"\"
CREATE OR REPLACE FUNCTION ash_trim_whitespace(arr text[])
RETURNS text[] AS $$
DECLARE
start_index INT = 1;
end_index INT = array_length(arr, 1);
BEGIN
WHILE start_index <= end_index AND arr[start_index] = '' LOOP
start_index := start_index + 1;
END LOOP;
WHILE end_index >= start_index AND arr[end_index] = '' LOOP
end_index := end_index - 1;
END LOOP;
IF start_index > end_index THEN
RETURN ARRAY[]::text[];
ELSE
RETURN arr[start_index : end_index];
END IF;
END; $$
LANGUAGE plpgsql
IMMUTABLE;
\"\"\")
"""
end
defp set_ash_functions(snapshot, installed_extensions) do
if "ash-functions" in installed_extensions do
Map.put(snapshot, :ash_functions_version, @latest_ash_functions_version)
else
snapshot
end
end
defp create_migrations(snapshots, opts) do
snapshots
|> Enum.group_by(& &1.repo)
@ -442,46 +308,15 @@ defmodule AshSqlite.MigrationGenerator do
end
operations
|> split_into_migrations()
|> Enum.each(fn operations ->
run_without_transaction? =
Enum.any?(operations, fn
%Operation.AddCustomIndex{index: %{concurrently: true}} ->
true
_ ->
false
end)
operations
|> organize_operations
|> build_up_and_down()
|> write_migration!(repo, opts, run_without_transaction?)
end)
|> organize_operations
|> build_up_and_down()
|> write_migration!(repo, opts, false)
create_new_snapshot(snapshots, repo_name(repo), opts)
end
end)
end
defp split_into_migrations(operations) do
operations
|> Enum.split_with(fn
%Operation.AddCustomIndex{index: %{concurrently: true}} ->
true
_ ->
false
end)
|> case do
{[], ops} ->
[ops]
{concurrent_indexes, ops} ->
[ops, concurrent_indexes]
end
end
defp add_order_to_operations({snapshot, operations}) do
operations_with_order = Enum.map(operations, &add_order_to_operation(&1, snapshot.attributes))
@ -828,13 +663,13 @@ defmodule AshSqlite.MigrationGenerator do
config = repo.config()
app = Keyword.fetch!(config, :otp_app)
if opts.migration_path do
opts.migration_path
else
Path.join([Mix.Project.deps_paths()[app] || File.cwd!(), "priv"])
end
|> Path.join(repo_name)
|> Path.join("migrations")
if opts.migration_path do
opts.migration_path
else
Path.join([Mix.Project.deps_paths()[app] || File.cwd!(), "priv"])
end
|> Path.join(repo_name)
|> Path.join("migrations")
end
defp repo_name(repo) do
@ -877,7 +712,7 @@ defmodule AshSqlite.MigrationGenerator do
|> Path.join(migration_name <> ".exs")
module_name =
Module.concat([repo, Migrations, Macro.camelize(last_part)])
Module.concat([repo, Migrations, Macro.camelize(last_part)])
module_attributes =
if run_without_transaction? do
@ -953,9 +788,9 @@ defmodule AshSqlite.MigrationGenerator do
snapshot_binary = snapshot_to_binary(snapshot)
snapshot_folder =
opts
|> snapshot_path(snapshot.repo)
|> Path.join(repo_name)
opts
|> snapshot_path(snapshot.repo)
|> Path.join(repo_name)
snapshot_file = Path.join(snapshot_folder, "#{snapshot.table}/#{timestamp()}.json")
@ -1104,9 +939,20 @@ defmodule AshSqlite.MigrationGenerator do
nil,
acc
) do
# this is kind of a hack
{has_to_be_in_this_phase, rest} =
Enum.split_with(rest, fn
%Operation.AddAttribute{table: ^table} -> true
_ -> false
end)
group_into_phases(
rest,
%Phase.Create{table: table, multitenancy: multitenancy},
%Phase.Create{
table: table,
multitenancy: multitenancy,
operations: has_to_be_in_this_phase
},
acc
)
end
@ -1240,18 +1086,6 @@ defmodule AshSqlite.MigrationGenerator do
true
end
defp after?(
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table,
multitenancy: multitenancy
},
%Operation.AddAttribute{table: table, attribute: %{source: source}}
) do
source in List.wrap(attribute_or_attributes) ||
(multitenancy.attribute && multitenancy.attribute in List.wrap(attribute_or_attributes))
end
defp after?(
%Operation.AddCustomIndex{
table: table
@ -1261,68 +1095,6 @@ defmodule AshSqlite.MigrationGenerator do
true
end
defp after?(
%Operation.AddCustomIndex{
table: table,
index: %{
concurrently: true
}
},
%Operation.AddCustomIndex{
table: table,
index: %{
concurrently: false
}
}
) do
true
end
defp after?(
%Operation.AddCheckConstraint{table: table, constraint: %{name: name}},
%Operation.RemoveCheckConstraint{
table: table,
constraint: %{
name: name
}
}
),
do: true
defp after?(
%Operation.RemoveCheckConstraint{
table: table,
constraint: %{
name: name
}
},
%Operation.AddCheckConstraint{table: table, constraint: %{name: name}}
),
do: false
defp after?(
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table
},
%Operation.AlterAttribute{table: table, new_attribute: %{source: source}}
) do
source in List.wrap(attribute_or_attributes)
end
defp after?(
%Operation.AddCheckConstraint{
constraint: %{attribute: attribute_or_attributes},
table: table
},
%Operation.RenameAttribute{
table: table,
new_attribute: %{source: source}
}
) do
source in List.wrap(attribute_or_attributes)
end
defp after?(
%Operation.RemoveUniqueIndex{table: table},
%Operation.AddUniqueIndex{table: table}
@ -1337,29 +1109,6 @@ defmodule AshSqlite.MigrationGenerator do
true
end
defp after?(
%Operation.RemoveCheckConstraint{
constraint: %{attribute: attributes},
table: table
},
%Operation.RemoveAttribute{table: table, attribute: %{source: source}}
) do
source in List.wrap(attributes)
end
defp after?(
%Operation.RemoveCheckConstraint{
constraint: %{attribute: attributes},
table: table
},
%Operation.RenameAttribute{
table: table,
old_attribute: %{source: source}
}
) do
source in List.wrap(attributes)
end
defp after?(%Operation.AlterAttribute{table: table}, %Operation.DropForeignKey{
table: table,
direction: :up
@ -1386,7 +1135,7 @@ defmodule AshSqlite.MigrationGenerator do
defp after?(%Operation.AddAttribute{table: table}, %Operation.CreateTable{
table: table
}) do
}) do
true
end
@ -1583,12 +1332,6 @@ defmodule AshSqlite.MigrationGenerator do
),
do: true
defp after?(%Operation.AddCheckConstraint{table: table}, %Operation.CreateTable{
table: table
}) do
true
end
defp after?(
%Operation.AlterAttribute{new_attribute: %{references: references}, table: table},
%{table: table}
@ -1596,9 +1339,6 @@ defmodule AshSqlite.MigrationGenerator do
when not is_nil(references),
do: true
defp after?(%Operation.AddCheckConstraint{}, _), do: true
defp after?(%Operation.RemoveCheckConstraint{}, _), do: true
defp after?(_, _), do: false
defp fetch_operations(snapshots, opts) do
@ -1619,7 +1359,6 @@ defmodule AshSqlite.MigrationGenerator do
identities: [],
custom_indexes: [],
custom_statements: [],
check_constraints: [],
table: snapshot.table,
repo: snapshot.repo,
base_filter: nil,
@ -1770,33 +1509,6 @@ defmodule AshSqlite.MigrationGenerator do
}
end)
constraints_to_add =
snapshot.check_constraints
|> Enum.reject(fn constraint ->
Enum.find(old_snapshot.check_constraints, fn old_constraint ->
old_constraint.check == constraint.check && old_constraint.name == constraint.name
end)
end)
|> Enum.map(fn constraint ->
%Operation.AddCheckConstraint{
constraint: constraint,
table: snapshot.table
}
end)
constraints_to_remove =
old_snapshot.check_constraints
|> Enum.reject(fn old_constraint ->
Enum.find(snapshot.check_constraints, fn constraint ->
old_constraint.check == constraint.check && old_constraint.name == constraint.name
end)
end)
|> Enum.map(fn old_constraint ->
%Operation.RemoveCheckConstraint{
constraint: old_constraint,
table: old_snapshot.table
}
end)
[
pkey_operations,
@ -1804,8 +1516,6 @@ defmodule AshSqlite.MigrationGenerator do
attribute_operations,
unique_indexes_to_add,
unique_indexes_to_rename,
constraints_to_remove,
constraints_to_add,
custom_indexes_to_add,
custom_indexes_to_remove,
custom_statements_to_add,
@ -1874,8 +1584,8 @@ defmodule AshSqlite.MigrationGenerator do
if must_drop_pkey? do
[
%Operation.RemovePrimaryKey{ table: snapshot.table},
%Operation.RemovePrimaryKeyDown{ table: snapshot.table}
%Operation.RemovePrimaryKey{table: snapshot.table},
%Operation.RemovePrimaryKeyDown{table: snapshot.table}
]
else
[]
@ -1921,32 +1631,9 @@ defmodule AshSqlite.MigrationGenerator do
add_attribute_events =
Enum.flat_map(attributes_to_add, fn attribute ->
if attribute.references do
reference_ops =
if attribute.references.deferrable do
[
%Operation.AlterDeferrability{
table: snapshot.table,
references: attribute.references,
direction: :up
},
%Operation.AlterDeferrability{
table: snapshot.table,
references: Map.get(attribute, :references),
direction: :down
}
]
else
[]
end
[
%Operation.AddAttribute{
attribute: Map.delete(attribute, :references),
table: snapshot.table
},
%Operation.AlterAttribute{
old_attribute: Map.delete(attribute, :references),
new_attribute: attribute,
attribute: attribute,
table: snapshot.table
},
%Operation.DropForeignKey{
@ -1955,7 +1642,7 @@ defmodule AshSqlite.MigrationGenerator do
multitenancy: Map.get(attribute, :multitenancy),
direction: :down
}
] ++ reference_ops
]
else
[
%Operation.AddAttribute{
@ -2137,9 +1824,9 @@ defmodule AshSqlite.MigrationGenerator do
repo_name = snapshot.repo |> Module.split() |> List.last() |> Macro.underscore()
folder =
opts
|> snapshot_path(snapshot.repo)
|> Path.join(repo_name)
opts
|> snapshot_path(snapshot.repo)
|> Path.join(repo_name)
snapshot_folder = Path.join(folder, snapshot.table)
@ -2281,9 +1968,7 @@ defmodule AshSqlite.MigrationGenerator do
|> Enum.uniq()
|> Enum.map(fn relationship ->
resource
|> do_snapshot(
relationship.context[:data_layer][:table]
)
|> do_snapshot(relationship.context[:data_layer][:table])
|> Map.update!(:identities, fn identities ->
identity_index_names = AshSqlite.DataLayer.Info.identity_index_names(resource)
@ -2342,7 +2027,6 @@ defmodule AshSqlite.MigrationGenerator do
attributes: attributes(resource, table),
identities: identities(resource),
table: table || AshSqlite.DataLayer.Info.table(resource),
check_constraints: check_constraints(resource),
custom_indexes: custom_indexes(resource),
custom_statements: custom_statements(resource),
repo: AshSqlite.DataLayer.Info.repo(resource),
@ -2365,48 +2049,6 @@ defmodule AshSqlite.MigrationGenerator do
|> Enum.any?(&(&1.type == :create))
end
defp check_constraints(resource) do
resource
|> AshSqlite.DataLayer.Info.check_constraints()
|> Enum.filter(& &1.check)
|> case do
[] ->
[]
constraints ->
base_filter = Ash.Resource.Info.base_filter(resource)
if base_filter && !AshSqlite.DataLayer.Info.base_filter_sql(resource) do
raise """
Cannot create a check constraint for a resource with a base filter without also configuring `base_filter_sql`.
You must provide the `base_filter_sql` option, or manually create add the check constraint to your migrations.
"""
end
constraints
end
|> Enum.map(fn constraint ->
attributes =
constraint.attribute
|> List.wrap()
|> Enum.map(fn attribute ->
attr =
resource
|> Ash.Resource.Info.attribute(attribute)
attr.source || attr.name
end)
%{
name: constraint.name,
attribute: attributes,
check: constraint.check,
base_filter: AshSqlite.DataLayer.Info.base_filter_sql(resource)
}
end)
end
defp custom_indexes(resource) do
resource
|> AshSqlite.DataLayer.Info.custom_indexes()
@ -2590,7 +2232,7 @@ defmodule AshSqlite.MigrationGenerator do
defp foreign_key?(relationship) do
Ash.DataLayer.data_layer(relationship.source) == AshSqlite.DataLayer &&
AshSqlite.DataLayer.Info.repo(relationship.source) ==
AshSqlite.DataLayer.Info.repo(relationship.destination)
AshSqlite.DataLayer.Info.repo(relationship.destination)
end
defp identities(resource) do
@ -2646,20 +2288,9 @@ defmodule AshSqlite.MigrationGenerator do
|> Enum.map(&Map.put(&1, :base_filter, AshSqlite.DataLayer.Info.base_filter_sql(resource)))
end
@uuid_functions [&Ash.UUID.generate/0, &Ecto.UUID.generate/0]
defp default(%{name: name, default: default}, resource, repo) when is_function(default) do
configured_default(resource, name) ||
cond do
default in @uuid_functions && "uuid-ossp" in (repo.config()[:installed_extensions] || []) ->
~S[fragment("uuid_generate_v4()")]
default == (&DateTime.utc_now/0) ->
~S[fragment("now()")]
true ->
"nil"
end
defp default(%{name: name, default: default}, resource, _repo) when is_function(default) do
configured_default(resource, name) || "nil"
end
defp default(%{name: name, default: {_, _, _}}, resource, _),
@ -2770,8 +2401,6 @@ defmodule AshSqlite.MigrationGenerator do
|> Map.update!(:custom_indexes, &load_custom_indexes/1)
|> Map.put_new(:custom_statements, [])
|> Map.update!(:custom_statements, &load_custom_statements/1)
|> Map.put_new(:check_constraints, [])
|> Map.update!(:check_constraints, &load_check_constraints/1)
|> Map.update!(:repo, &String.to_atom/1)
|> Map.put_new(:multitenancy, %{
attribute: nil,
@ -2782,16 +2411,6 @@ defmodule AshSqlite.MigrationGenerator do
|> Map.put_new(:base_filter, nil)
end
defp load_check_constraints(constraints) do
Enum.map(constraints, fn constraint ->
Map.update!(constraint, :attribute, fn attribute ->
attribute
|> List.wrap()
|> Enum.map(&String.to_atom/1)
end)
end)
end
defp load_custom_indexes(custom_indexes) do
Enum.map(custom_indexes || [], fn custom_index ->
custom_index

View file

@ -72,7 +72,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
defmodule AddAttribute do
@moduledoc false
defstruct [:attribute, :table, :multitenancy, :old_multitenancy]
defstruct [:attribute, :table, :multitenancy, :old_multitenancy]
import Helper
@ -273,7 +273,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
multitenancy: multitenancy,
old_attribute: old_attribute,
new_attribute: attribute
}) do
}) do
type_or_reference =
if AshSqlite.MigrationGenerator.has_reference?(multitenancy, attribute) and
Map.get(old_attribute, :references) != Map.get(attribute, :references) do
@ -295,7 +295,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
destination_attribute: reference_attribute
} = reference
} = attribute
) do
) do
with_match =
if destination_attribute != reference_attribute do
"with: [#{as_atom(source_attribute)}: :#{as_atom(destination_attribute)}], match: :full"
@ -327,7 +327,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
destination_attribute: destination_attribute
} = reference
} = attribute
) do
) do
size =
if attribute[:size] do
"size: #{attribute[:size]}"
@ -360,7 +360,7 @@ defmodule AshSqlite.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, :table, :multitenancy, :direction, no_phase: true]
import Helper
@ -420,7 +420,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
defmodule RemoveAttribute do
@moduledoc false
defstruct [:attribute, :table, :multitenancy, :old_multitenancy, commented?: true]
defstruct [:attribute, :table, :multitenancy, :old_multitenancy, commented?: true]
def up(%{attribute: attribute, commented?: true}) do
"""
@ -587,7 +587,6 @@ defmodule AshSqlite.MigrationGenerator.Operation do
join([
option(:name, index.name),
option(:unique, index.unique),
option(:concurrently, index.concurrently),
option(:using, index.using),
option(:where, index.where),
option(:include, index.include)
@ -619,7 +618,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
@moduledoc false
defstruct [:table, no_phase: true]
def up(%{ table: table}) do
def up(%{table: table}) do
"drop constraint(#{inspect(table)}, \"#{table}_pkey\")"
end
@ -637,7 +636,7 @@ defmodule AshSqlite.MigrationGenerator.Operation do
end
def down(%{table: table}) do
"drop constraint(#{inspect(table)}, \"#{table}_pkey\")"
"drop constraint(#{inspect(table)}, \"#{table}_pkey\")"
end
end
@ -687,7 +686,6 @@ defmodule AshSqlite.MigrationGenerator.Operation do
join([
option(:name, index.name),
option(:unique, index.unique),
option(:concurrently, index.concurrently),
option(:using, index.using),
option(:where, index.where),
option(:include, index.include)
@ -783,59 +781,4 @@ defmodule AshSqlite.MigrationGenerator.Operation do
end
end
end
defmodule AddCheckConstraint do
@moduledoc false
defstruct [:table, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{
constraint: %{
name: name,
check: check,
base_filter: base_filter
},
table: table
}) do
if base_filter do
"create constraint(:#{as_atom(table)}, :#{as_atom(name)}, #{join(["check: \"#{base_filter} AND #{check}\")"])}"
else
"create constraint(:#{as_atom(table)}, :#{as_atom(name)}, #{join(["check: \"#{check}\")"])}"
end
end
def down(%{
constraint: %{name: name},
table: table
}) do
"drop_if_exists constraint(:#{as_atom(table)}, #{join([":#{as_atom(name)}"])})"
end
end
defmodule RemoveCheckConstraint do
@moduledoc false
defstruct [:table, :constraint, :multitenancy, :old_multitenancy, no_phase: true]
import Helper
def up(%{constraint: %{name: name}, table: table}) do
"drop_if_exists constraint(:#{as_atom(table)}, #{join([":#{as_atom(name)}"])})"
end
def down(%{
constraint: %{
name: name,
check: check,
base_filter: base_filter
},
table: table
}) do
if base_filter do
"create constraint(:#{as_atom(table)}, :#{as_atom(name)}, #{join(["check: \"#{base_filter} AND #{check}\")"])}"
else
"create constraint(:#{as_atom(table)}, :#{as_atom(name)}, #{join(["check: \"#{check}\")"])}"
end
end
end
end

View file

@ -8,17 +8,17 @@ defmodule AshSqlite.MigrationGenerator.Phase do
import AshSqlite.MigrationGenerator.Operation.Helper, only: [as_atom: 1]
def up(%{table: table, operations: operations}) do
opts = ""
opts = ""
"create table(:#{as_atom(table)}, primary_key: false#{opts}) do\n" <>
Enum.map_join(operations, "\n", fn operation -> operation.__struct__.up(operation) end) <>
"\nend"
"create table(:#{as_atom(table)}, primary_key: false#{opts}) do\n" <>
Enum.map_join(operations, "\n", fn operation -> operation.__struct__.up(operation) end) <>
"\nend"
end
def down(%{ table: table}) do
opts = ""
def down(%{table: table}) do
opts = ""
"drop table(:#{as_atom(table)}#{opts})"
"drop table(:#{as_atom(table)}#{opts})"
end
end
@ -37,11 +37,11 @@ defmodule AshSqlite.MigrationGenerator.Phase do
if body == "" do
""
else
opts = ""
opts = ""
"alter table(:#{as_atom(table)}#{opts}) do\n" <>
body <>
"\nend"
"alter table(:#{as_atom(table)}#{opts}) do\n" <>
body <>
"\nend"
end
end

View file

@ -123,7 +123,6 @@ defmodule AshSqlite.MixHelpers do
opts[:migrations_path] || repo.config()[:migrations_path] || derive_migrations_path(repo)
end
def derive_migrations_path(repo) do
config = repo.config()
priv = config[:priv] || "priv/#{repo |> Module.split() |> List.last() |> Macro.underscore()}"

View file

@ -90,6 +90,6 @@ defmodule Mix.Tasks.AshSqlite.GenerateMigrations do
|> Keyword.put(:format, !opts[:no_format])
|> Keyword.delete(:no_format)
AshSqlite.MigrationGenerator.generate(apis, opts)
AshSqlite.MigrationGenerator.generate(apis, opts)
end
end

View file

@ -102,13 +102,13 @@ defmodule Mix.Tasks.AshSqlite.Migrate do
|> AshSqlite.MixHelpers.delete_arg("--apis")
|> AshSqlite.MixHelpers.delete_arg("--migrations-path")
for repo <- repos do
Mix.Task.run(
"ecto.migrate",
repo_args ++ rest_opts ++ ["--migrations-path", migrations_path(opts, repo)]
)
for repo <- repos do
Mix.Task.run(
"ecto.migrate",
repo_args ++ rest_opts ++ ["--migrations-path", migrations_path(opts, repo)]
)
Mix.Task.reenable("ecto.migrate")
end
Mix.Task.reenable("ecto.migrate")
end
end
end

View file

@ -67,13 +67,13 @@ defmodule Mix.Tasks.AshSqlite.Rollback do
|> AshSqlite.MixHelpers.delete_arg("--apis")
|> AshSqlite.MixHelpers.delete_arg("--migrations-path")
for repo <- repos do
Mix.Task.run(
"ecto.rollback",
repo_args ++ rest_opts ++ ["--migrations-path", migrations_path(opts, repo)]
)
for repo <- repos do
Mix.Task.run(
"ecto.rollback",
repo_args ++ rest_opts ++ ["--migrations-path", migrations_path(opts, repo)]
)
Mix.Task.reenable("ecto.rollback")
end
Mix.Task.reenable("ecto.rollback")
end
end
end

View file

@ -50,7 +50,6 @@ defmodule AshSqlite.Repo do
def installed_extensions, do: []
def migrations_path, do: nil
def default_prefix, do: "public"
def override_migration_type(type), do: type
def min_pg_version, do: 10
@ -59,7 +58,6 @@ defmodule AshSqlite.Repo do
config
|> Keyword.put(:installed_extensions, installed_extensions())
|> Keyword.put(:migrations_path, migrations_path())
|> Keyword.put(:default_prefix, default_prefix())
{:ok, new_config}
end
@ -164,7 +162,6 @@ defmodule AshSqlite.Repo do
defoverridable init: 2,
on_transaction_begin: 1,
installed_extensions: 0,
default_prefix: 0,
override_migration_type: 1,
min_pg_version: 0
end

View file

@ -12,7 +12,7 @@ defmodule AshSqlite.Transformers.VerifyRepo do
match?({:error, _}, Code.ensure_compiled(repo)) ->
{:error, "Could not find repo module #{repo}"}
repo.__adapter__() != Ecto.Adapters.Sqlite3 ->
repo.__adapter__() != Ecto.Adapters.SQLite3 ->
{:error, "Expected a repo using the sqlite adapter `Ecto.Adapters.SQLite3`"}
true ->

View file

@ -43,7 +43,10 @@ defmodule AshSqlite.MixProject do
if Mix.env() == :test do
def application() do
[applications: [:ecto, :ecto_sql, :jason, :ash, :postgrex], mod: {AshSqlite.TestApp, []}]
[
applications: [:ecto, :ecto_sql, :ecto_sqlite3, :jason, :ash],
mod: {AshSqlite.TestApp, []}
]
end
end
@ -145,7 +148,6 @@ defmodule AshSqlite.MixProject do
],
Introspection: [
AshSqlite.DataLayer.Info,
AshSqlite.CheckConstraint,
AshSqlite.CustomExtension,
AshSqlite.CustomIndex,
AshSqlite.Reference,
@ -173,7 +175,6 @@ defmodule AshSqlite.MixProject do
{:ecto_sqlite3, "~> 0.11"},
{:ecto, "~> 3.9"},
{:jason, "~> 1.0"},
{:postgrex, ">= 0.0.0"},
{:ash, ash_version("~> 2.14 and >= 2.14.18")},
{:git_ops, "~> 2.5", only: [:dev, :test]},
{:ex_doc, "~> 0.22", only: [:dev, :test], runtime: false},

View file

@ -34,7 +34,6 @@
"nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"},
"postgrex": {:hex, :postgrex, "0.17.2", "a3ec9e3239d9b33f1e5841565c4eb200055c52cc0757a22b63ca2d529bbe764c", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "80a918a9e9531d39f7bd70621422f3ebc93c01618c645f2d91306f50041ed90c"},
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
"sourceror": {:hex, :sourceror, "0.14.0", "b6b8552d0240400d66b6f107c1bab7ac1726e998efc797f178b7b517e928e314", [:mix], [], "hexpm", "809c71270ad48092d40bbe251a133e49ae229433ce103f762a2373b7a10a8d8b"},
"spark": {:hex, :spark, "1.1.39", "f143b84a5b796bf2d83ec8fb4793ee9e66e67510c40d785f9a67050bb88e7677", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d71bc26014c7e7abcdcf553f4cf7c5a5ff96f8365b1e20be3768ce503aafb203"},

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "boolean",
"source": "is_active",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "user_id",
"references": {
"name": "accounts_user_id_fkey",
"table": "users",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "accounts",
"hash": "D9E9F02FE7FAA0A6245C109E6F60A30C6E1B869222B0D8F233F6DE0BB61208FC",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,70 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "first_name",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "last_name",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "map",
"source": "bio",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": [
"array",
"text"
],
"source": "badges",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "authors",
"hash": "046788B41EC7355A3649FB5A7FFFDE7EA5AC7DB0A8D661455AE0EE47EDCB3954",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "score",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "resource_id",
"references": {
"name": "comment_ratings_resource_id_fkey",
"table": "comments",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": false,
"destination_attribute_default": "nil",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "comment_ratings",
"hash": "EA05462C665EF2CFA57E07758FEF9AB9244EBB75C3A75471BA457195E6018BFB",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,117 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "title",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "likes",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "utc_datetime_usec",
"source": "arbitrary_timestamp",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "utc_datetime_usec",
"source": "created_at",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "post_id",
"references": {
"name": "special_name_fkey",
"table": "posts",
"on_delete": "delete",
"on_update": "update",
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "author_id",
"references": {
"name": "comments_author_id_fkey",
"table": "authors",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "comments",
"hash": "F8021D06DAA6DBAA04DD4560E664905182EA32587D0087EC9F8BD145E40A8432",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,37 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": true
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "title",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "integer_posts",
"hash": "A06710150BA7BDCA09FBD107B3F6B5174EA424D0317A4452BDB7BEF3DA1435F0",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,101 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "name",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "code",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "must_be_present",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "role",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "organization_id",
"references": {
"name": "managers_organization_id_fkey",
"table": "orgs",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "managers",
"hash": "40BBC76EA6172776136FE98035C92E23033D6F758F642B0BBE86691E8016D1B2",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [
{
"name": "uniq_code",
"keys": [
"code"
],
"base_filter": null,
"index_name": "managers_uniq_code_index"
}
],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,37 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "name",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "orgs",
"hash": "20DCD0C640F7BA5F64BEB236F05D79A091E1F3390507929060BAB2D38241C284",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,87 @@
{
"attributes": [
{
"default": "\"active\"",
"size": null,
"type": "text",
"source": "state",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "source_post_id",
"references": {
"name": "post_links_source_post_id_fkey",
"table": "posts",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "destination_post_id",
"references": {
"name": "post_links_destination_post_id_fkey",
"table": "posts",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": true,
"allow_nil?": false,
"generated?": false
}
],
"table": "post_links",
"hash": "2583037B16689EDA7F8414C379C6D315D1E86BD4ED4FA6861E744F629E7CE8A3",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [
{
"name": "unique_link",
"keys": [
"source_post_id",
"destination_post_id"
],
"base_filter": null,
"index_name": "post_links_unique_link_index"
}
],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "score",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "resource_id",
"references": {
"name": "post_ratings_resource_id_fkey",
"table": "posts",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": false,
"destination_attribute_default": "nil",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "post_ratings",
"hash": "DE6056BA23D22810D95CCDB6FDF5F0FE9F558124FC8D9B08861B8F59099C0032",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,47 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "utc_datetime_usec",
"source": "time",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "browser",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "post_id",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
}
],
"table": "post_views",
"hash": "25FC1D03DE747F86A8894536121520D288728618469978D90571337038FF2A23",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,274 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "title",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "score",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "boolean",
"source": "public",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "category",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "\"sponsored\"",
"size": null,
"type": "text",
"source": "type",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "bigint",
"source": "price",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "\"0\"",
"size": null,
"type": "decimal",
"source": "decimal",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "status",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "status",
"source": "status_enum",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": [
"array",
"float"
],
"source": "point",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "map",
"source": "stuff",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "uniq_one",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "uniq_two",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "uniq_custom_one",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "uniq_custom_two",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "utc_datetime_usec",
"source": "created_at",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "utc_datetime_usec",
"source": "updated_at",
"references": null,
"primary_key?": false,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "organization_id",
"references": {
"name": "posts_organization_id_fkey",
"table": "orgs",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "author_id",
"references": {
"name": "posts_author_id_fkey",
"table": "authors",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "posts",
"hash": "53F023DD86C40482AB21DFD50A457BE677EAE3627B62F581357B24B4FE1D8611",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [
{
"message": "dude what the heck",
"name": null,
"table": null,
"include": null,
"fields": [
"uniq_custom_one",
"uniq_custom_two"
],
"where": null,
"unique": true,
"using": null
}
],
"base_filter": "type = 'sponsored'",
"identities": [
{
"name": "uniq_one_and_two",
"keys": [
"uniq_one",
"uniq_two"
],
"base_filter": "type = 'sponsored'",
"index_name": "posts_uniq_one_and_two_index"
}
],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "text",
"source": "description",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "author_id",
"references": {
"name": "profile_author_id_fkey",
"table": "authors",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "profile",
"hash": "866B88FC262E758B5FA5C81951A000F31D0F0AB0AE6FFA2872C39EBC242CD097",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,62 @@
{
"attributes": [
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "id",
"references": null,
"primary_key?": true,
"allow_nil?": false,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "boolean",
"source": "is_active",
"references": null,
"primary_key?": false,
"allow_nil?": true,
"generated?": false
},
{
"default": "nil",
"size": null,
"type": "uuid",
"source": "organization_id",
"references": {
"name": "users_organization_id_fkey",
"table": "orgs",
"on_delete": null,
"on_update": null,
"destination_attribute_generated": null,
"destination_attribute_default": null,
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"destination_attribute": "id",
"primary_key?": true,
"deferrable": false
},
"primary_key?": false,
"allow_nil?": true,
"generated?": false
}
],
"table": "users",
"hash": "EBC30A0BFC23F11FEBED0038376C50027BEAF7FFE7B896CCFD20A09F37B39617",
"repo": "Elixir.AshSqlite.TestRepo",
"multitenancy": {
"global": null,
"attribute": null,
"strategy": null
},
"custom_indexes": [],
"base_filter": null,
"identities": [],
"custom_statements": [],
"has_create_action": true
}

View file

@ -0,0 +1,231 @@
defmodule AshSqlite.TestRepo.Migrations.MigrateResources1 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_sqlite.generate_migrations`
"""
use Ecto.Migration
def up do
create table(:users, primary_key: false) do
add :organization_id,
references(:orgs, column: :id, name: "users_organization_id_fkey", type: :uuid)
add :is_active, :boolean
add :id, :uuid, null: false, primary_key: true
end
create table(:profile, primary_key: false) do
add :author_id,
references(:authors, column: :id, name: "profile_author_id_fkey", type: :uuid)
add :description, :text
add :id, :uuid, null: false, primary_key: true
end
create table(:posts, primary_key: false) do
add :author_id, references(:authors, column: :id, name: "posts_author_id_fkey", type: :uuid)
add :organization_id,
references(:orgs, column: :id, name: "posts_organization_id_fkey", type: :uuid)
add :updated_at, :utc_datetime_usec, null: false
add :created_at, :utc_datetime_usec, null: false
add :uniq_custom_two, :text
add :uniq_custom_one, :text
add :uniq_two, :text
add :uniq_one, :text
add :stuff, :map
add :point, {:array, :float}
add :status_enum, :status
add :status, :text
add :decimal, :decimal, default: "0"
add :price, :bigint
add :type, :text, default: "sponsored"
add :category, :text
add :public, :boolean
add :score, :bigint
add :title, :text
add :id, :uuid, null: false, primary_key: true
end
create table(:post_views, primary_key: false) do
add :post_id, :uuid, null: false
add :browser, :text
add :time, :utc_datetime_usec, null: false
end
create table(:post_ratings, primary_key: false) do
add :resource_id,
references(:posts, column: :id, name: "post_ratings_resource_id_fkey", type: :uuid)
add :score, :bigint
add :id, :uuid, null: false, primary_key: true
end
create table(:post_links, primary_key: false) do
add :destination_post_id,
references(:posts,
column: :id,
name: "post_links_destination_post_id_fkey",
type: :uuid
),
primary_key: true,
null: false
add :source_post_id,
references(:posts, column: :id, name: "post_links_source_post_id_fkey", type: :uuid),
primary_key: true,
null: false
add :state, :text, default: "active"
end
create unique_index(:post_links, [:source_post_id, :destination_post_id],
name: "post_links_unique_link_index"
)
create table(:orgs, primary_key: false) do
add :name, :text
add :id, :uuid, null: false, primary_key: true
end
create table(:managers, primary_key: false) do
add :organization_id,
references(:orgs, column: :id, name: "managers_organization_id_fkey", type: :uuid)
add :role, :text
add :must_be_present, :text, null: false
add :code, :text, null: false
add :name, :text
add :id, :uuid, null: false, primary_key: true
end
create unique_index(:managers, [:code], name: "managers_uniq_code_index")
create table(:integer_posts, primary_key: false) do
add :title, :text
add :id, :bigserial, null: false, primary_key: true
end
create table(:comments, primary_key: false) do
add :author_id,
references(:authors, column: :id, name: "comments_author_id_fkey", type: :uuid)
add :post_id,
references(:posts,
column: :id,
name: "special_name_fkey",
type: :uuid,
on_delete: :delete_all,
on_update: :update_all
)
add :created_at, :utc_datetime_usec, null: false
add :arbitrary_timestamp, :utc_datetime_usec
add :likes, :bigint
add :title, :text
add :id, :uuid, null: false, primary_key: true
end
create table(:comment_ratings, primary_key: false) do
add :resource_id,
references(:comments,
column: :id,
name: "comment_ratings_resource_id_fkey",
type: :uuid
)
add :score, :bigint
add :id, :uuid, null: false, primary_key: true
end
create table(:authors, primary_key: false) do
add :badges, {:array, :text}
add :bio, :map
add :last_name, :text
add :first_name, :text
add :id, :uuid, null: false, primary_key: true
end
create index(:posts, ["uniq_custom_one", "uniq_custom_two"], unique: true)
create unique_index(:posts, [:uniq_one, :uniq_two],
where: "type = 'sponsored'",
name: "posts_uniq_one_and_two_index"
)
create table(:accounts, primary_key: false) do
add :user_id, references(:users, column: :id, name: "accounts_user_id_fkey", type: :uuid)
add :is_active, :boolean
add :id, :uuid, null: false, primary_key: true
end
end
def down do
drop constraint(:accounts, "accounts_user_id_fkey")
drop table(:accounts)
drop_if_exists unique_index(:posts, [:uniq_one, :uniq_two],
name: "posts_uniq_one_and_two_index"
)
drop_if_exists index(:posts, ["uniq_custom_one", "uniq_custom_two"],
name: "posts_uniq_custom_one_uniq_custom_two_index"
)
drop table(:authors)
drop constraint(:comment_ratings, "comment_ratings_resource_id_fkey")
drop table(:comment_ratings)
drop constraint(:comments, "special_name_fkey")
drop constraint(:comments, "comments_author_id_fkey")
drop table(:comments)
drop table(:integer_posts)
drop_if_exists unique_index(:managers, [:code], name: "managers_uniq_code_index")
drop constraint(:managers, "managers_organization_id_fkey")
drop table(:managers)
drop table(:orgs)
drop_if_exists unique_index(:post_links, [:source_post_id, :destination_post_id],
name: "post_links_unique_link_index"
)
drop constraint(:post_links, "post_links_source_post_id_fkey")
drop constraint(:post_links, "post_links_destination_post_id_fkey")
drop table(:post_links)
drop constraint(:post_ratings, "post_ratings_resource_id_fkey")
drop table(:post_ratings)
drop table(:post_views)
drop constraint(:posts, "posts_organization_id_fkey")
drop constraint(:posts, "posts_author_id_fkey")
drop table(:posts)
drop constraint(:profile, "profile_author_id_fkey")
drop table(:profile)
drop constraint(:users, "users_organization_id_fkey")
drop table(:users)
end
end

View file

@ -1,57 +0,0 @@
defmodule AshSqlite.Test.LockTest do
use AshSqlite.RepoCase, async: false
alias AshSqlite.Test.{Api, Post}
require Ash.Query
setup do
Application.put_env(:ash, :disable_async?, true)
on_exit(fn ->
Application.put_env(:ash, :disable_async?, false)
AshSqlite.TestNoSandboxRepo.delete_all(Post)
end)
end
test "lock conflicts raise appropriate errors" do
post =
Post
|> Ash.Changeset.for_create(:create, %{title: "locked"})
|> Ash.Changeset.set_context(%{data_layer: %{repo: AshSqlite.TestNoSandboxRepo}})
|> Api.create!()
task1 =
Task.async(fn ->
AshSqlite.TestNoSandboxRepo.transaction(fn ->
Post
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|> Ash.Query.set_context(%{data_layer: %{repo: AshSqlite.TestNoSandboxRepo}})
|> Ash.Query.filter(id == ^post.id)
|> Api.read!()
:timer.sleep(1000)
:ok
end)
end)
task2 =
Task.async(fn ->
try do
AshSqlite.TestNoSandboxRepo.transaction(fn ->
:timer.sleep(100)
Post
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|> Ash.Query.set_context(%{data_layer: %{repo: AshSqlite.TestNoSandboxRepo}})
|> Ash.Query.filter(id == ^post.id)
|> Api.read!()
end)
rescue
e ->
{:error, e}
end
end)
assert [{:ok, :ok}, {:error, %Ash.Error.Invalid{errors: [%Ash.Error.Invalid.Unavailable{}]}}] =
Task.await_many([task1, task2], :infinity)
end
end

View file

@ -93,7 +93,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
Mix.shell(Mix.Shell.Process)
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -151,50 +151,6 @@ defmodule AshSqlite.MigrationGeneratorTest do
end
end
describe "custom_indexes with `concurrently: true`" do
setup do
on_exit(fn ->
File.rm_rf!("test_snapshots_path")
File.rm_rf!("test_migration_path")
end)
defposts do
sqlite do
custom_indexes do
# need one without any opts
index([:title], concurrently: true)
end
end
attributes do
uuid_primary_key(:id)
attribute(:title, :string)
end
end
defapi([Post])
Mix.shell(Mix.Shell.Process)
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
end
test "it creates multiple migration files" do
assert [_, custom_index_migration] =
Enum.sort(Path.wildcard("test_migration_path/**/*_migrate_resources*.exs"))
file = File.read!(custom_index_migration)
assert file =~ ~S[@disable_ddl_transaction true]
assert file =~ ~S<create index(:posts, ["title"], concurrently: true)>
end
end
describe "creating follow up migrations" do
setup do
on_exit(fn ->
@ -217,7 +173,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
Mix.shell(Mix.Shell.Process)
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -245,7 +201,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -274,7 +230,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -300,7 +256,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
send(self(), {:mix_shell_input, :yes?, true})
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -325,7 +281,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
send(self(), {:mix_shell_input, :yes?, false})
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -353,7 +309,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
send(self(), {:mix_shell_input, :yes?, true})
send(self(), {:mix_shell_input, :prompt, "subject"})
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -383,7 +339,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
send(self(), {:mix_shell_input, :yes?, false})
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -414,7 +370,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Post2])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -449,7 +405,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
Mix.shell(Mix.Shell.Process)
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -528,7 +484,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Post2])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -563,7 +519,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Post2])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -598,7 +554,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Post2])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -650,133 +606,6 @@ defmodule AshSqlite.MigrationGeneratorTest do
end
end
describe "check constraints" do
setup do
on_exit(fn ->
File.rm_rf!("test_snapshots_path")
File.rm_rf!("test_migration_path")
end)
end
test "when added, the constraint is created" do
defposts do
attributes do
uuid_primary_key(:id)
attribute(:price, :integer)
end
sqlite do
check_constraints do
check_constraint(:price, "price_must_be_positive", check: "price > 0")
end
end
end
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
assert file =
"test_migration_path/**/*_migrate_resources*.exs"
|> Path.wildcard()
|> Enum.sort()
|> Enum.at(0)
|> File.read!()
assert file =~
~S[create constraint(:posts, :price_must_be_positive, check: "price > 0")]
defposts do
attributes do
uuid_primary_key(:id)
attribute(:price, :integer)
end
sqlite do
check_constraints do
check_constraint(:price, "price_must_be_positive", check: "price > 1")
end
end
end
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
assert file =
"test_migration_path/**/*_migrate_resources*.exs"
|> Path.wildcard()
|> Enum.sort()
|> Enum.at(1)
|> File.read!()
assert [_, down] = String.split(file, "def down do")
assert [_, remaining] =
String.split(down, "drop_if_exists constraint(:posts, :price_must_be_positive)")
assert remaining =~
~S[create constraint(:posts, :price_must_be_positive, check: "price > 0")]
end
test "when removed, the constraint is dropped before modification" do
defposts do
attributes do
uuid_primary_key(:id)
attribute(:price, :integer)
end
sqlite do
check_constraints do
check_constraint(:price, "price_must_be_positive", check: "price > 0")
end
end
end
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
defposts do
attributes do
uuid_primary_key(:id)
attribute(:price, :integer)
end
end
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
format: false
)
assert file =
"test_migration_path/**/*_migrate_resources*.exs"
|> Path.wildcard()
|> Enum.sort()
|> Enum.at(1)
assert File.read!(file) =~
~S[drop_if_exists constraint(:posts, :price_must_be_positive)]
end
end
describe "polymorphic resources" do
setup do
on_exit(fn ->
@ -835,7 +664,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Comment])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -880,7 +709,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -989,7 +818,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
Mix.shell(Mix.Shell.Process)
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,
@ -1028,7 +857,7 @@ defmodule AshSqlite.MigrationGeneratorTest do
defapi([Post, Comment])
AshSqlite.MigrationGenerator.generate(Api,
AshSqlite.MigrationGenerator.generate(Api,
snapshot_path: "test_snapshots_path",
migration_path: "test_migration_path",
quiet: true,

View file

@ -18,17 +18,9 @@ defmodule AshSqlite.Test.Post do
repo(AshSqlite.TestRepo)
base_filter_sql("type = 'sponsored'")
check_constraints do
check_constraint(:price, "price_must_be_positive",
message: "yo, bad price",
check: "price > 0"
)
end
custom_indexes do
index([:uniq_custom_one, :uniq_custom_two],
unique: true,
concurrently: true,
message: "dude what the heck"
)
end
@ -46,7 +38,7 @@ defmodule AshSqlite.Test.Post do
end
read :paginated do
pagination(offset?: true, required?: true, countable: true)
pagination(offset?: true, required?: true)
end
create :create do

View file

@ -1,13 +0,0 @@
defmodule AshSqlite.TestNoSandboxRepo do
@moduledoc false
use AshSqlite.Repo,
otp_app: :ash_sqlite
def on_transaction_begin(data) do
send(self(), data)
end
def installed_extensions do
["ash-functions", AshSqlite.TestCustomExtension]
end
end

View file

@ -6,8 +6,4 @@ defmodule AshSqlite.TestRepo do
def on_transaction_begin(data) do
send(self(), data)
end
def installed_extensions do
["ash-functions", AshSqlite.TestCustomExtension]
end
end

View file

@ -2,4 +2,5 @@ ExUnit.start()
ExUnit.configure(stacktrace_depth: 100)
AshSqlite.TestRepo.start_link()
AshSqlite.TestNoSandboxRepo.start_link()
Ecto.Adapters.SQL.Sandbox.mode(AshSqlite.TestRepo, :manual)

View file

@ -1,10 +0,0 @@
{
"ash_functions_version": 1,
"installed": [
"ash-functions",
"uuid-ossp",
"pg_trgm",
"citext",
"demo-functions_v1"
]
}