diff --git a/documentation/dsls/DSL-AshPostgres.DataLayer.md b/documentation/dsls/DSL-AshPostgres.DataLayer.md index 305a925f..4ad2f0b3 100644 --- a/documentation/dsls/DSL-AshPostgres.DataLayer.md +++ b/documentation/dsls/DSL-AshPostgres.DataLayer.md @@ -44,7 +44,7 @@ end | [`migration_types`](#postgres-migration_types){: #postgres-migration_types } | `keyword` | `[]` | A keyword list of attribute names to the ecto migration type that should be used for that attribute. Only necessary if you need to override the defaults. | | [`migration_defaults`](#postgres-migration_defaults){: #postgres-migration_defaults } | `keyword` | `[]` | A keyword list of attribute names to the ecto migration default that should be used for that attribute. The string you use will be placed verbatim in the migration. Use fragments like `fragment(\\"now()\\")`, or for `nil`, use `\\"nil\\"`. | | [`calculations_to_sql`](#postgres-calculations_to_sql){: #postgres-calculations_to_sql } | `keyword` | | A keyword list of calculations and their SQL representation. Used when creating unique indexes for identities over calculations | -| [`identity_wheres_to_sql`](#postgres-identity_wheres_to_sql){: #postgres-identity_wheres_to_sql } | `keyword` | | A keyword list of identity names and the SQL representation of their `where` clause. Used when creating unique indexes for identities over calculations | +| [`identity_wheres_to_sql`](#postgres-identity_wheres_to_sql){: #postgres-identity_wheres_to_sql } | `keyword` | | A keyword list of identity names and the SQL representation of their `where` clause. See `AshPostgres.DataLayer.Info.identity_wheres_to_sql/1` for more details. | | [`base_filter_sql`](#postgres-base_filter_sql){: #postgres-base_filter_sql } | `String.t` | | A raw sql version of the base_filter, e.g `representative = true`. Required if trying to create a unique constraint on a resource with a base_filter | | [`simple_join_first_aggregates`](#postgres-simple_join_first_aggregates){: #postgres-simple_join_first_aggregates } | `list(atom)` | `[]` | A list of `:first` type aggregate names that can be joined to using a simple join. Use when you have a `:first` aggregate that uses a to-many relationship , but your `filter` statement ensures that there is only one result. Optimizes the generated query. | | [`skip_unique_indexes`](#postgres-skip_unique_indexes){: #postgres-skip_unique_indexes } | `atom \| list(atom)` | `[]` | Skip generating unique indexes when generating migrations | diff --git a/lib/data_layer.ex b/lib/data_layer.ex index a0be3ab6..670567ba 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -315,7 +315,7 @@ defmodule AshPostgres.DataLayer do identity_wheres_to_sql: [ type: :keyword_list, doc: - "A keyword list of identity names and the SQL representation of their `where` clause. Used when creating unique indexes for identities over calculations" + "A keyword list of identity names and the SQL representation of their `where` clause. See `AshPostgres.DataLayer.Info.identity_wheres_to_sql/1` for more details." ], base_filter_sql: [ type: :string, @@ -2668,9 +2668,11 @@ defmodule AshPostgres.DataLayer do case identity do %{name: name, where: where} when not is_nil(where) -> AshPostgres.DataLayer.Info.identity_where_to_sql(resource, name) || - raise( - "Must provide an entry for :#{identity.name} in `postgres.identity_wheres_to_sql` to use it as an upsert_identity" - ) + raise(""" + Must provide an entry for :#{identity.name} in `postgres.identity_wheres_to_sql` to use it as an upsert_identity. + + See https://hexdocs.pm/ash_postgres/AshPostgres.DataLayer.Info.html#identity_wheres_to_sql/1 for an example. + """) _ -> nil diff --git a/lib/data_layer/info.ex b/lib/data_layer/info.ex index 7f66ff2c..696439ae 100644 --- a/lib/data_layer/info.ex +++ b/lib/data_layer/info.ex @@ -23,11 +23,39 @@ defmodule AshPostgres.DataLayer.Info do calculations_to_sql(resource)[calc] end - @doc "A keyword list of identity names to the sql representation of their where clauses" + @doc """ + A keyword list of identity names to the literal SQL string representation of + the `where` clause portion of identity's partial unique index. + + For example, given the following identity for a resource: + + identities do + identity :active, [:status] do + where expr(status == "active") + end + end + + An appropriate `identity_wheres_to_sql` would need to be made to generate the + correct migration for the partial index used by the identity: + + postgres do + ... + + identity_wheres_to_sql active: "status = 'active'" + end + """ + @spec identity_wheres_to_sql(Ash.Resource.t()) :: keyword(String.t()) def identity_wheres_to_sql(resource) do Extension.get_opt(resource, [:postgres], :identity_wheres_to_sql, []) end + @doc """ + Returns the literal SQL for the `where` clause given a resource and an + identity name. + + See `identity_wheres_to_sql/1` for more details. + """ + @spec identity_where_to_sql(Ash.Resource.t(), atom()) :: String.t() | nil def identity_where_to_sql(resource, identity) do identity_wheres_to_sql(resource)[identity] end diff --git a/lib/migration_generator/migration_generator.ex b/lib/migration_generator/migration_generator.ex index 110725fc..062bf348 100644 --- a/lib/migration_generator/migration_generator.ex +++ b/lib/migration_generator/migration_generator.ex @@ -3006,9 +3006,11 @@ defmodule AshPostgres.MigrationGenerator do where: if identity.where do AshPostgres.DataLayer.Info.identity_where_to_sql(resource, identity.name) || - raise( - "Must provide an entry for :#{identity.name} in `postgres.identity_wheres_to_sql`, or skip this identity with `postgres.skip_unique_indexes`" - ) + raise(""" + Must provide an entry for :#{identity.name} in `postgres.identity_wheres_to_sql`, or skip this identity with `postgres.skip_unique_indexes`. + + See https://hexdocs.pm/ash_postgres/AshPostgres.DataLayer.Info.html#identity_wheres_to_sql/1 for an example. + """) end } end)