ash-project / ash_postgres

The PostgreSQL data layer for Ash Framework
https://hexdocs.pm/ash_postgres
MIT License
139 stars 73 forks source link

An action combining two change commands blows up #348

Closed olivermt closed 3 months ago

olivermt commented 3 months ago

Describe the bug

** (Ash.Error.Unknown.UnknownError) ** (Ecto.SubQueryError) the following exception happened when compiling a subquery.

    ** (Ecto.QueryError) field `__new_approved` in `select` does not exist in schema Safari.Virtual.CesiumAsset in query:

    from c0 in Safari.Virtual.CesiumAsset,
      as: 0,
      where: type(as(0).id, {:parameterized, Ash.Type.Integer.EctoType, []}) ==
      type(^325, {:parameterized, Ash.Type.Integer.EctoType, []}),
      limit: ^1,
      select: merge(
      %{
        __new_approved: ^true,
        __new_state:
          fragment(
            "(CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END)",
            is_nil(
              fragment(
                "(CASE WHEN ? THEN ? ELSE ? END)",
                type(
                  as(0).state,
                  {:parameterized, Ash.Type.Atom.EctoType,
                   one_of: [
                     :failed,
                     :quality_assurance,
                     :initialized,
                     :uploading,
                     :processing,
                     :archiving,
                     :downloading,
                     :placeable,
                     :complete
                   ]}
                ) in ^["quality_assurance"] or type(^nil, :boolean),
                ^"placeable",
                fragment(
                  "ash_raise_error(?::jsonb, ?)",
                  fragment(
                    "(jsonb_build_object('exception', ?::text, 'input', jsonb_build_object(?::text, ?::jsonb, ?::text, ?::jsonb, ?::text, ?)))",
                    ^"AshStateMachine.Errors.NoMatchingTransition",
                    ^"target",
                    ^"placeable",
                    ^"action",
                    ^"approve_quality",
                    ^"old_state",
                    type(
                      as(0).state,
                      {:parameterized, Ash.Type.Atom.EctoType,
                       one_of: [
                         :failed,
                         :quality_assurance,
                         :initialized,
                         :uploading,
                         :processing,
                         :archiving,
                         :downloading,
                         :placeable,
                         :complete
                       ]}
                    )
                  ),
                  map(c0, [
                    :id,
                    :cesium_asset_id,
                    :cesium_bucket_credentials,
                    :cesium_upload_callback_url,
                    :cesium_progress,
                    :cesium_archive_id,
                    :cesium_archive_byte_size,
                    :utm_data,
                    :location,
                    :default_camera,
                    :tileset_url,
                    :approved,
                    :error,
                    :virtual_outcrop_model_id,
                    :state
                  ]).state
                )
              )
            ),
            fragment(
              "ash_raise_error(?::jsonb, ?)",
              ^"{\"input\":{\"type\":\"attribute\",\"resource\":\"Elixir.Safari.Virtual.CesiumAsset\",\"field\":\"state\"},\"exception\":\"Ash.Error.Changes.Required\"}",
              type(
                map(c0, [
                  :id,
                  :cesium_asset_id,
                  :cesium_bucket_credentials,
                  :cesium_upload_callback_url,
                  :cesium_progress,
                  :cesium_archive_id,
                  :cesium_archive_byte_size,
                  :utm_data,
                  :location,
                  :default_camera,
                  :tileset_url,
                  :approved,
                  :error,
                  :virtual_outcrop_model_id,
                  :state
                ]).state,
                {:parameterized, Ash.Type.Atom.EctoType,
                 one_of: [
                   :failed,
                   :quality_assurance,
                   :initialized,
                   :uploading,
                   :processing,
                   :archiving,
                   :downloading,
                   :placeable,
                   :complete
                 ]}
              )
            ),
            type(
              as(0).state,
              {:parameterized, Ash.Type.Atom.EctoType,
               one_of: [
                 :failed,
                 :quality_assurance,
                 :initialized,
                 :uploading,
                 :processing,
                 :archiving,
                 :downloading,
                 :placeable,
                 :complete
               ]}
            ) in ^["quality_assurance"] or type(^nil, :boolean),
            type(
              ^"placeable",
              {:parameterized, Ash.Type.Atom.EctoType,
               one_of: [
                 :failed,
                 :quality_assurance,
                 :initialized,
                 :uploading,
                 :processing,
                 :archiving,
                 :downloading,
                 :placeable,
                 :complete
               ]}
            ),
            fragment(
              "ash_raise_error(?::jsonb, ?)",
              fragment(
                "(jsonb_build_object('exception', ?::text, 'input', jsonb_build_object(?::text, ?::jsonb, ?::text, ?::jsonb, ?::text, ?)))",
                ^"AshStateMachine.Errors.NoMatchingTransition",
                ^"target",
                ^"placeable",
                ^"action",
                ^"approve_quality",
                ^"old_state",
                type(
                  as(0).state,
                  {:parameterized, Ash.Type.Atom.EctoType,
                   one_of: [
                     :failed,
                     :quality_assurance,
                     :initialized,
                     :uploading,
                     :processing,
                     :archiving,
                     :downloading,
                     :placeable,
                     :complete
                   ]}
                )
              ),
              type(
                map(c0, [
                  :id,
                  :cesium_asset_id,
                  :cesium_bucket_credentials,
                  :cesium_upload_callback_url,
                  :cesium_progress,
                  :cesium_archive_id,
                  :cesium_archive_byte_size,
                  :utm_data,
                  :location,
                  :default_camera,
                  :tileset_url,
                  :approved,
                  :error,
                  :virtual_outcrop_model_id,
                  :state
                ]).state,
                {:parameterized, Ash.Type.Atom.EctoType,
                 one_of: [
                   :failed,
                   :quality_assurance,
                   :initialized,
                   :uploading,
                   :processing,
                   :archiving,
                   :downloading,
                   :placeable,
                   :complete
                 ]}
              )
            )
          )
      },
      map(c0, [
        :id,
        :cesium_asset_id,
        :cesium_bucket_credentials,
        :cesium_upload_callback_url,
        :cesium_progress,
        :cesium_archive_id,
        :cesium_archive_byte_size,
        :utm_data,
        :location,
        :default_camera,
        :tileset_url,
        :approved,
        :error,
        :virtual_outcrop_model_id,
        :state
      ])
    )

    Did you mean `approved`?

The subquery originated from the following query:

from c0 in subquery(from c0 in Safari.Virtual.CesiumAsset,
  as: 0,
  where: type(as(0).id, {:parameterized, Ash.Type.Integer.EctoType, []}) ==
  type(^325, {:parameterized, Ash.Type.Integer.EctoType, []}),
  limit: ^1,
  select: merge(
  %{
    __new_approved: ^true,
    __new_state:
      fragment(
        "(CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END)",
        is_ni (truncated)
[error] something_went_wrong: SomethingWentWrong | Something went wrong. Error id: c6a72422-db1c-4418-b41e-d8326ebb7a4b
[info] Sent 500 in 117ms

To Reproduce

    update :approve_quality do
      # validate attribute_equals(:state, :quality_assurance)
      change transition_state(:placeable)
      change set_attribute(:approved, true)
    end

Expected behavior Not to blow up and work

** Runtime

Additional context If you run either change alone, it works, only blows up if you have both.

zachdaniel commented 3 months ago

I believe this issue is fixed in main, and latest of ash_sql. Please try it out, we can reopen if not.