diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index 9b2c4aa6..13602ec6 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -1158,6 +1158,7 @@ defmodule AshPostgres.MigrationGenerator do destination_attribute: merge_uniq!(references, table, :destination_attribute, name), deferrable: merge_uniq!(references, table, :deferrable, name), index?: merge_uniq!(references, table, :index?, name), + index_where: merge_uniq!(references, table, :index_where, name), destination_attribute_default: merge_uniq!(references, table, :destination_attribute_default, name), destination_attribute_generated: @@ -2610,8 +2611,11 @@ defmodule AshPostgres.MigrationGenerator do old_attribute -> new_index? = attribute.references && attribute.references[:index?] old_index? = old_attribute.references && old_attribute.references[:index?] + new_index_where = attribute.references && attribute.references[:index_where] + old_index_where = old_attribute.references && old_attribute.references[:index_where] old_index? != new_index? || + (new_index? && old_index_where != new_index_where) || (new_index? && multitenancy_changed?) end end) @@ -2620,6 +2624,7 @@ defmodule AshPostgres.MigrationGenerator do table: snapshot.table, schema: snapshot.schema, source: attribute.source, + where: attribute.references[:index_where], multitenancy: snapshot.multitenancy } end) @@ -2640,13 +2645,19 @@ defmodule AshPostgres.MigrationGenerator do attribute && old_attribute[:references][:index?] && attribute[:references][:index?] && multitenancy_changed? - has_removed_index? || attribute_doesnt_exist? || multitenancy_change_requires_rewrite? + index_where_change_requires_rewrite? = + attribute && old_attribute[:references][:index?] && attribute[:references][:index?] && + old_attribute[:references][:index_where] != attribute[:references][:index_where] + + has_removed_index? || attribute_doesnt_exist? || multitenancy_change_requires_rewrite? || + index_where_change_requires_rewrite? end) |> Enum.map(fn attribute -> %Operation.RemoveReferenceIndex{ table: snapshot.table, schema: snapshot.schema, source: attribute.source, + where: attribute.references[:index_where], multitenancy: snapshot.multitenancy, old_multitenancy: old_snapshot.multitenancy } @@ -3274,11 +3285,13 @@ defmodule AshPostgres.MigrationGenerator do old_refs |> clean_references_for_comparison() |> Map.delete(:index?) + |> Map.delete(:index_where) new_without_index = new_refs |> clean_references_for_comparison() |> Map.delete(:index?) + |> Map.delete(:index_where) old_without_index != new_without_index end @@ -3308,6 +3321,9 @@ defmodule AshPostgres.MigrationGenerator do {left, right} = normalize_attribute_reference_schema_move(left, right, schema_moves) + left = remove_reference_index_options(left) + right = remove_reference_index_options(right) + left = if ignore_names? do Map.drop(left, [:source, :name]) @@ -3325,6 +3341,13 @@ defmodule AshPostgres.MigrationGenerator do left != right end + defp remove_reference_index_options(%{references: references} = attribute) + when is_map(references) do + %{attribute | references: Map.drop(references, [:index?, :index_where])} + end + + defp remove_reference_index_options(attribute), do: attribute + defp normalize_attribute_reference_schema_move(left, right, schema_moves) do old_refs = Map.get(left, :references) new_refs = Map.get(right, :references) @@ -4345,6 +4368,8 @@ defmodule AshPostgres.MigrationGenerator do destination_attribute: destination_attribute_source, deferrable: configured_reference.deferrable, index?: configured_reference.index?, + index_where: + reference_index_where(configured_reference.index_where, attribute.source), multitenancy: multitenancy(relationship.destination), on_delete: configured_reference.on_delete, on_update: configured_reference.on_update, @@ -4367,6 +4392,10 @@ defmodule AshPostgres.MigrationGenerator do end) end + defp reference_index_where(:not_nil, source), do: "#{source} IS NOT NULL" + defp reference_index_where(index_where, _source) when is_binary(index_where), do: index_where + defp reference_index_where(nil, _source), do: nil + defp configured_reference(resource, table, attribute, relationship) do ref = resource @@ -4379,6 +4408,7 @@ defmodule AshPostgres.MigrationGenerator do match_type: nil, deferrable: false, index?: false, + index_where: nil, schema: relationship.context[:data_layer][:schema] || AshPostgres.DataLayer.Info.schema(relationship.destination) || @@ -4796,6 +4826,7 @@ defmodule AshPostgres.MigrationGenerator do |> Map.update!(:on_delete, &(&1 && load_references_on_delete(&1))) |> Map.update!(:on_update, &(&1 && maybe_to_atom(&1))) |> Map.put_new(:index?, false) + |> Map.put_new(:index_where, nil) |> Map.put_new(:match_with, nil) |> Map.put_new(:match_type, nil) |> Map.update!( diff --git a/lib/migration_generator/operation.ex b/lib/migration_generator/operation.ex index 904391b9..57e1831f 100644 --- a/lib/migration_generator/operation.ex +++ b/lib/migration_generator/operation.ex @@ -1156,13 +1156,14 @@ defmodule AshPostgres.MigrationGenerator.Operation do defmodule AddReferenceIndex do @moduledoc false - defstruct [:table, :schema, :source, :multitenancy, no_phase: true] + defstruct [:table, :schema, :source, :where, :multitenancy, no_phase: true] import Helper def up(%{ source: source, table: table, schema: schema, + where: where, multitenancy: multitenancy }) do keys = @@ -1176,7 +1177,8 @@ defmodule AshPostgres.MigrationGenerator.Operation do opts = join([ - option(:prefix, schema) + option(:prefix, schema), + option(:where, where) ]) if opts == "" do @@ -1378,7 +1380,7 @@ defmodule AshPostgres.MigrationGenerator.Operation do defmodule RemoveReferenceIndex do @moduledoc false - defstruct [:schema, :table, :source, :multitenancy, :old_multitenancy, no_phase: true] + defstruct [:schema, :table, :source, :where, :multitenancy, :old_multitenancy, no_phase: true] import Helper def up(operation) do diff --git a/lib/reference.ex b/lib/reference.ex index da69d165..c54a324d 100644 --- a/lib/reference.ex +++ b/lib/reference.ex @@ -13,6 +13,7 @@ defmodule AshPostgres.Reference do :match_type, :deferrable, :index?, + :index_where, :__spark_metadata__, ignore?: false ] @@ -71,6 +72,11 @@ defmodule AshPostgres.Reference do type: :boolean, default: false, doc: "Whether to create or not a corresponding index" + ], + index_where: [ + type: {:or, [{:one_of, [:not_nil]}, :string]}, + doc: + "A condition to use for the corresponding partial index. Use `:not_nil` to exclude rows where the reference is nil." ] ] end diff --git a/mix.lock b/mix.lock index 5ad3a457..67ba0e77 100644 --- a/mix.lock +++ b/mix.lock @@ -4,19 +4,19 @@ "benchee": {:hex, :benchee, "1.5.1", "b95cbc36c4b98969a5c592a246e171041eb683c56bad1cb4f49a3b081ba66087", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.1", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a539301f8dfd4efc5c5123bfb9d47ebde20092a863a5b5b16c2a60d2243dfce7"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, - "crux": {:hex, :crux, "0.1.3", "c698dee09d811678dcddad11a02a832c6bff100f1a7aee49ac44c87485bdbac8", [:mix], [{:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "04188ea9c1cee13e3ef132417200765857402dcc581f45a8a7862eec3b0530ff"}, - "db_connection": {:hex, :db_connection, "2.10.1", "d5465f6bcc125c1b8981c1dbf23c193ca16f446ec0b25832dc174f74f18be510", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "18ed94c6e627b4bf452dbd4df61b69a35a1e768525140bc1917b7a685026a6a3"}, + "crux": {:hex, :crux, "0.1.4", "1fa21f5ca886d3498f83871a3ef19379b80abf8cfcf2ed32007e80279e714f1e", [:mix], [{:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "ff7d880cf732d82360aa81e439b8d4831cd30768061c9233f90c97e10162b9c0"}, + "db_connection": {:hex, :db_connection, "2.10.2", "ae391e803a5adff104da913c2fc1c0c14a37f8b10001dcef568796e1fb7bf95c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "510b14482330f1af6490a2fa0efd8d4f1435d1529b165647df22ac0f2df0fa93"}, "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, "deep_merge": {:hex, :deep_merge, "1.0.2", "476aa7ea61c54de96220051b998d893869069094da65b96101aebf79416f8a1e", [:mix], [], "hexpm", "737a53cdc9758fedbb608bdc213969e65729466c4ef3cd8e8726d0335dff116c"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "earmark_parser": {:hex, :earmark_parser, "1.4.45", "cba8369ab2a1342e419bc2760eec731b17be828941dcf494045d44766227e1d5", [:mix], [], "hexpm", "d3ec045bf122965db20c0bdb420e19ee1415843135327124918473feb4b328e8"}, - "ecto": {:hex, :ecto, "3.14.0", "2fa64521eebfcb2670d907a86e4ad947290e9933706bb315e6fb5c21b172cb26", [:mix], [{:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "130d69ffb4285f9ce4792b65dfbb994fd13ea4cbc3cbea2524b199aa3de84af3"}, + "ecto": {:hex, :ecto, "3.14.1", "7b740d87bdf45996aa0c2c2e081640906f10caa7ce5ba328fd294c7d49d0cc6f", [:mix], [{:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "24b991956796700f467d0a3ef3d303138a3ef9ddddf8b98f43758ee067b20a30"}, "ecto_dev_logger": {:hex, :ecto_dev_logger, "0.15.0", "df5a997ffb17dca9011556857a0f5b7d8cd53ca7c452ef98828664b6e48d4400", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:geo, "~> 3.5 or ~> 4.0", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.17", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "b2c807d7d599a4fcf288139851c09262333b193bdb41f8d65f515853d117e88a"}, "ecto_sql": {:hex, :ecto_sql, "3.14.0", "06446ab8410d2f85bfbb80857ee224ab3b693700cbb38f6535d507449a627b2e", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.14.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.8", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f4d8d36faf294c9417b5a37ec7ac8217ee2abdef5fcf197ba690f361548d3949"}, "eflame": {:hex, :eflame, "1.0.1", "0664d287e39eef3c413749254b3af5f4f8b00be71c1af67d325331c4890be0fc", [:mix], [], "hexpm", "e0b08854a66f9013129de0b008488f3411ae9b69b902187837f994d7a99cf04e"}, "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, - "ex_ast": {:hex, :ex_ast, "0.12.5", "b9f0e2e1d0cce79e860a5f6fd8ae82fd8ac1f7e7182d0b45a0985cdd2df1cd20", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "c7ef42abba201cfe0bb4377eeb63911c581e51552e925a4bd52c26c786a20f98"}, + "ex_ast": {:hex, :ex_ast, "0.12.10", "1126eb2afe3218e7a8a53ce85adf1056c528c88fbb5adcbe0191b647b842b377", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "e03f668c4354e3a1382c3d762c0fcc82ca9670f6f37e62b9097ce752be6adf39"}, "ex_check": {:hex, :ex_check, "0.16.0", "07615bef493c5b8d12d5119de3914274277299c6483989e52b0f6b8358a26b5f", [:mix], [], "hexpm", "4d809b72a18d405514dda4809257d8e665ae7cf37a7aee3be6b74a34dec310f5"}, "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, @@ -24,7 +24,7 @@ "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"}, "git_ops": {:hex, :git_ops, "2.10.0", "225780d8dcf9ef3393d26fa8d41d6a454a71149393c040e4b708c7c0b9c2b0f1", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.27 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "acd4a542eb425a58ce54505de69be704af3d0ef23c9b8cf9a3299d88e5d098ae"}, "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"}, - "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, + "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, "igniter": {:hex, :igniter, "0.8.2", "28621d9d2559c86a0249a195d9a7c55e8967b781850feff0bce6c4013b75f1c3", [:mix], [{:ex_ast, "~> 0.5", [hex: :ex_ast, repo: "hexpm", optional: false]}, {:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4.5", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "89146ad3fba21f3ea10873ae509fc760618623d4578e4723929a9dd1532aa30f"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, @@ -32,7 +32,7 @@ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, - "mint": {:hex, :mint, "1.9.0", "d6f534c2a3e98b2a8cc749b4796eb77e9e3af79a76f96e4c74035a827de0d318", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "007154c7d8c43916aed3c93afd1f11aebbaa9c5ff4b7ba55ebe0d17ee0296042"}, + "mint": {:hex, :mint, "1.9.2", "6e89e698d69cc29be001afd02c2cf4bae2d0994efe69a2ced7aab440d71584f9", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d8e952b432fdac2321f570d29a68b9eb2664dc80a7a2ef9464531a5425abf222"}, "mix_audit": {:hex, :mix_audit, "2.1.5", "c0f77cee6b4ef9d97e37772359a187a166c7a1e0e08b50edf5bf6959dfe5a016", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "87f9298e21da32f697af535475860dc1d3617a010e0b418d2ec6142bc8b42d69"}, "mix_test_interactive": {:hex, :mix_test_interactive, "5.1.0", "a2edd66806f06f6a0dbb92f023126a7e4ecdf02f7d34e3bb16f76886b79bed43", [:mix], [{:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:process_tree, ">= 0.1.3", [hex: :process_tree, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "0df8ab74c176040acd78939e92a5328775bf9f414a0f0472372cce852ee548cf"}, "multigraph": {:hex, :multigraph, "0.16.1-mg.4", "2bbe149f5411b0e3bf0624c7bf2e3da2738efeac2f9a67bbbcb807ab171f0a76", [:mix], [], "hexpm", "b9f3e2577cef4658eeedf97c76d22a86d33a7aab702a93c1da9c122e849e9037"}, @@ -40,7 +40,7 @@ "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "owl": {:hex, :owl, "0.13.1", "1ec4a5dea170465f0e90c502c203079224516bc0cbd599281c8667b3c6ef8848", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "351e768af8f2edc575cdaab1a5a2f6d6381be591758a026c701c703145508a0c"}, - "postgrex": {:hex, :postgrex, "0.22.2", "4aec14df2a72722aee92492566edbeeb44e233ecb86b1915d03136297ef1385d", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.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", "8946382ddb06294f56026ac4278b3cc212bac8a2c82ed68b4087819ed1abc53b"}, + "postgrex": {:hex, :postgrex, "0.22.3", "bf65941737ee7a9adbe4a64c91080310d11703da343e8ac9188aacb9eb9f6f02", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0 or ~> 3.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", "f018c13752b2b46e8d35d7e2d84c3276557cbfd880769109021a1d0ee36c1cfe"}, "process_tree": {:hex, :process_tree, "0.3.0", "0eb58ec68f3d22a4f36040b0374469464c95fae5465c011b55bea01649b0bd70", [:mix], [], "hexpm", "6cb3b7be9c7d74b28a9f6e0f03115d5953e6bbda44be2b6fd9e667020870eb86"}, "reactor": {:hex, :reactor, "1.0.2", "79e4e81d016ab0016afd10bb4c18cb3a574f08f10f8e53be5f08ce27f8eed541", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:multigraph, "~> 0.16.1-mg.2", [hex: :multigraph, repo: "hexpm", optional: false]}, {:spark, ">= 2.3.3 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "19fd55aaaadaae28f55133351051c25d4ac217f99e3e5a67940cc4a321e3948e"}, "req": {:hex, :req, "0.6.2", "b9b2024f35bcf60a92cc8cad2eaaf9d4e7aace463ff74be1afe5986830184413", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cc9cd30a2ddd04989929b887178e1610c940456d962c6c3a52df6146d2eef9bf"}, @@ -52,7 +52,7 @@ "spitfire": {:hex, :spitfire, "0.3.13", "edd207b065eaec57acc5484097d0aa3e97fe4246168c54e67a9af040b8dee4c1", [:mix], [], "hexpm", "3601be88ceed4967b584e96444de3e1d12d6555ae0864a7390b9cd5332d134b4"}, "splode": {:hex, :splode, "0.3.1", "9843c54f84f71b7833fec3f0be06c3cfb5be6b35960ee195ea4fad84b1c25030", [:mix], [], "hexpm", "8f2309b6ec2ecbb01435656429ed1d9ed04ba28797a3280c3b0d1217018ecfbd"}, "statistex": {:hex, :statistex, "1.1.1", "73612aa7f79e53c30569be065fd121e380f1cf57bc4c2da5b41be9246da18df9", [:mix], [], "hexpm", "310c4b49b34adf683de3103639006bed233ab54c08a4add65a531448e653857c"}, - "stream_data": {:hex, :stream_data, "1.3.0", "bde37905530aff386dea1ddd86ecbf00e6642dc074ceffc10b7d4e41dfd6aac9", [:mix], [], "hexpm", "3cc552e286e817dca43c98044c706eec9318083a1480c52ae2688b08e2936e3c"}, + "stream_data": {:hex, :stream_data, "1.4.0", "026f929db613aabea6208012ae9b8970d3fd5f88b3bdf26831bc536f98c42036", [:mix], [], "hexpm", "2b0ee3a340dcce1c8cf6302a763ee757d1e01c54d6e16d9069062509d68b1dc9"}, "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, diff --git a/test/migration_generator_test.exs b/test/migration_generator_test.exs index 25b9218b..f52f11bd 100644 --- a/test/migration_generator_test.exs +++ b/test/migration_generator_test.exs @@ -2387,6 +2387,166 @@ defmodule AshPostgres.MigrationGeneratorTest do assert File.read!(file) =~ ~S{create index(:posts, [:post_id])} end + test "references generate partial indexes with index_where: :not_nil", %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource PartialIndexPost, "partial_index_posts" do + attributes do + uuid_primary_key(:id) + end + end + + defresource PartialIndexComment, "partial_index_comments" do + attributes do + uuid_primary_key(:id) + end + + relationships do + belongs_to(:post, PartialIndexPost, public?: true) + end + + postgres do + references do + reference(:post, index?: true, index_where: :not_nil) + end + end + end + + defmodule PartialIndexDomain do + use Ash.Domain + + resources do + resource(PartialIndexPost) + resource(PartialIndexComment) + end + end + + AshPostgres.MigrationGenerator.generate(PartialIndexDomain, + snapshot_path: snapshot_path, + migration_path: migration_path, + quiet: true, + format: false, + auto_name: true + ) + + assert [file] = + Path.wildcard("#{migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + + assert File.read!(file) =~ + ~S{create index(:partial_index_comments, [:post_id], where: "post_id IS NOT NULL")} + + assert [snapshot_file] = + Path.wildcard("#{snapshot_path}/**/partial_index_comments/*.json") + + snapshot = snapshot_file |> File.read!() |> Jason.decode!() + post_id = Enum.find(snapshot["attributes"], &(&1["source"] == "post_id")) + + assert post_id["references"]["index_where"] == "post_id IS NOT NULL" + end + + test "changing only reference index_where replaces the index without changing the foreign key", + %{ + snapshot_path: snapshot_path, + migration_path: migration_path + } do + defresource FullIndexPost, "index_where_posts" do + attributes do + uuid_primary_key(:id) + end + end + + defresource FullIndexComment, "index_where_comments" do + attributes do + uuid_primary_key(:id) + end + + relationships do + belongs_to(:post, FullIndexPost, public?: true) + end + + postgres do + references do + reference(:post, index?: true) + end + end + end + + defmodule FullIndexDomain do + use Ash.Domain + + resources do + resource(FullIndexPost) + resource(FullIndexComment) + end + end + + AshPostgres.MigrationGenerator.generate(FullIndexDomain, + snapshot_path: snapshot_path, + migration_path: migration_path, + quiet: true, + format: false, + auto_name: true + ) + + defresource IndexWherePartialPost, "index_where_posts" do + attributes do + uuid_primary_key(:id) + end + end + + defresource IndexWherePartialComment, "index_where_comments" do + attributes do + uuid_primary_key(:id) + end + + relationships do + belongs_to(:post, IndexWherePartialPost, public?: true) + end + + postgres do + references do + reference(:post, index?: true, index_where: "post_id IS NOT NULL") + end + end + end + + defmodule IndexWherePartialDomain do + use Ash.Domain + + resources do + resource(IndexWherePartialPost) + resource(IndexWherePartialComment) + end + end + + AshPostgres.MigrationGenerator.generate(IndexWherePartialDomain, + snapshot_path: snapshot_path, + migration_path: migration_path, + quiet: true, + format: false, + auto_name: true + ) + + [_, file] = + Path.wildcard("#{migration_path}/**/*_migrate_resources*.exs") + |> Enum.reject(&String.contains?(&1, "extensions")) + |> Enum.sort() + + content = File.read!(file) + + assert content =~ ~S{drop_if_exists index(:index_where_comments, [:post_id])} + + assert content =~ + ~S{create index(:index_where_comments, [:post_id], where: "post_id IS NOT NULL")} + + refute content =~ + ~S{drop constraint(:index_where_comments, "index_where_comments_post_id_fkey")} + + refute content =~ ~S{modify :post_id, references(} + end + test "changing only reference index? does not drop and re-add foreign key (issue #611)", %{ snapshot_path: snapshot_path, migration_path: migration_path @@ -2511,6 +2671,9 @@ defmodule AshPostgres.MigrationGeneratorTest do refute content =~ ~S{modify :post_id, references(}, "migration should not modify references when only index? changed (issue #611)" + + refute content =~ ~S{modify :post_id,}, + "migration should not modify the attribute when only index? changes (issue #611)" end test "references with deferrable modifications generate changes with the correct schema", %{ @@ -6832,6 +6995,7 @@ defmodule AshPostgres.MigrationGeneratorTest do refute up =~ "references(:referenced_schema_move_authors" end end + describe "resources with migrate? false (#585)" do test "warns that resources were skipped due to migrate? false", %{ snapshot_path: snapshot_path, @@ -6879,8 +7043,7 @@ defmodule AshPostgres.MigrationGeneratorTest do output = shell_output() assert output =~ "No changes detected" - assert output =~ "migrate?" - assert output =~ "SkippedMigrateResource" + assert output =~ "Some resources have `migrate?` set to `false` and were skipped" end end end