From 56f3a5eb2fdbbbcff21c96559f9f7e8754f39bab Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Thu, 2 May 2024 21:29:21 -0400 Subject: [PATCH] fix: fix calculate when exprs aren't dynamics test: add test for datetimes --- lib/data_layer.ex | 10 + .../test_repo/posts/20240503012410.json | 352 ++++++++++++++++++ .../20240503012410_migrate_resources21.exs | 21 ++ test/bulk_update_test.exs | 14 + test/support/resources/post.ex | 1 + 5 files changed, 398 insertions(+) create mode 100644 priv/resource_snapshots/test_repo/posts/20240503012410.json create mode 100644 priv/test_repo/migrations/20240503012410_migrate_resources21.exs diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 73097c47..020ad370 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -1543,6 +1543,16 @@ defmodule AshPostgres.DataLayer do {dynamics, query} = Enum.reduce(expressions, {[], query}, fn expression, {dynamics, query} -> {dynamic, acc} = AshSql.Expr.dynamic_expr(query, expression, query.__ash_bindings__) + + dynamic = + case dynamic do + %Ecto.Query.DynamicExpr{} -> + dynamic + + other -> + Ecto.Query.dynamic(^other) + end + {[dynamic | dynamics], AshSql.Bindings.merge_expr_accumulator(query, acc)} end) diff --git a/priv/resource_snapshots/test_repo/posts/20240503012410.json b/priv/resource_snapshots/test_repo/posts/20240503012410.json new file mode 100644 index 00000000..238c6218 --- /dev/null +++ b/priv/resource_snapshots/test_repo/posts/20240503012410.json @@ -0,0 +1,352 @@ +{ + "attributes": [ + { + "default": "fragment(\"gen_random_uuid()\")", + "size": null, + "type": "uuid", + "source": "id", + "references": null, + "allow_nil?": false, + "generated?": false, + "primary_key?": true + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "title_column", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "utc_datetime_usec", + "source": "datetime", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "bigint", + "source": "score", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "boolean", + "source": "public", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "citext", + "source": "category", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "\"sponsored\"", + "size": null, + "type": "text", + "source": "type", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "bigint", + "source": "price", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "\"0\"", + "size": null, + "type": "decimal", + "source": "decimal", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "status", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "status", + "source": "status_enum", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": [ + "array", + "float" + ], + "source": "point", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "custom_point", + "source": "composite_point", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "map", + "source": "stuff", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": [ + "array", + "map" + ], + "source": "list_of_stuff", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "uniq_one", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "uniq_two", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "uniq_custom_one", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "text", + "source": "uniq_custom_two", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": [ + "array", + "text" + ], + "source": "list_containing_nils", + "references": null, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "created_at", + "references": null, + "allow_nil?": false, + "generated?": false, + "primary_key?": false + }, + { + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", + "size": null, + "type": "utc_datetime_usec", + "source": "updated_at", + "references": null, + "allow_nil?": false, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "uuid", + "source": "organization_id", + "references": { + "name": "posts_organization_id_fkey", + "table": "orgs", + "schema": "public", + "on_delete": null, + "multitenancy": { + "global": null, + "attribute": null, + "strategy": null + }, + "primary_key?": true, + "destination_attribute": "id", + "on_update": null, + "deferrable": false, + "match_type": null, + "match_with": null, + "destination_attribute_default": null, + "destination_attribute_generated": null + }, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + }, + { + "default": "nil", + "size": null, + "type": "uuid", + "source": "author_id", + "references": { + "name": "posts_author_id_fkey", + "table": "authors", + "schema": "public", + "on_delete": null, + "multitenancy": { + "global": null, + "attribute": null, + "strategy": null + }, + "primary_key?": true, + "destination_attribute": "id", + "on_update": null, + "deferrable": false, + "match_type": null, + "match_with": null, + "destination_attribute_default": null, + "destination_attribute_generated": null + }, + "allow_nil?": true, + "generated?": false, + "primary_key?": false + } + ], + "table": "posts", + "hash": "CAD4281EFCDF0328EEC1C473F0041F4DCD0DB6431C3DF44540EA67F4FA4511FE", + "repo": "Elixir.AshPostgres.TestRepo", + "identities": [ + { + "name": "uniq_one_and_two", + "keys": [ + "uniq_one", + "uniq_two" + ], + "base_filter": "type = 'sponsored'", + "all_tenants?": false, + "index_name": "posts_uniq_one_and_two_index" + } + ], + "schema": null, + "check_constraints": [ + { + "name": "price_must_be_positive", + "check": "price > 0", + "attribute": [ + "price" + ], + "base_filter": "type = 'sponsored'" + } + ], + "custom_indexes": [ + { + "message": "dude what the heck", + "name": null, + "table": null, + "include": null, + "prefix": null, + "fields": [ + { + "type": "atom", + "value": "uniq_custom_one" + }, + { + "type": "atom", + "value": "uniq_custom_two" + } + ], + "where": null, + "unique": true, + "all_tenants?": false, + "concurrently": true, + "error_fields": [ + "uniq_custom_one", + "uniq_custom_two" + ], + "nulls_distinct": true, + "using": null + } + ], + "base_filter": "type = 'sponsored'", + "multitenancy": { + "global": null, + "attribute": null, + "strategy": null + }, + "custom_statements": [], + "has_create_action": true +} \ No newline at end of file diff --git a/priv/test_repo/migrations/20240503012410_migrate_resources21.exs b/priv/test_repo/migrations/20240503012410_migrate_resources21.exs new file mode 100644 index 00000000..70781d33 --- /dev/null +++ b/priv/test_repo/migrations/20240503012410_migrate_resources21.exs @@ -0,0 +1,21 @@ +defmodule AshPostgres.TestRepo.Migrations.MigrateResources21 do + @moduledoc """ + Updates resources based on their most recent snapshots. + + This file was autogenerated with `mix ash_postgres.generate_migrations` + """ + + use Ecto.Migration + + def up do + alter table(:posts) do + add(:datetime, :utc_datetime_usec) + end + end + + def down do + alter table(:posts) do + remove(:datetime) + end + end +end diff --git a/test/bulk_update_test.exs b/test/bulk_update_test.exs index b8a953b3..26bbd65f 100644 --- a/test/bulk_update_test.exs +++ b/test/bulk_update_test.exs @@ -20,6 +20,20 @@ defmodule AshPostgres.BulkUpdateTest do assert Enum.all?(posts, &String.ends_with?(&1.title, "_stuff")) end + test "bulk updates can set datetimes" do + Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create) + + now = DateTime.utc_now() + + Ash.bulk_update!(Post, :update, %{datetime: now}) + + posts = Ash.read!(Post) + + assert Enum.all?(posts, fn post -> + DateTime.compare(post.datetime, now) == :eq + end) + end + test "a map can be given as input" do Ash.bulk_create!([%{title: "fred"}, %{title: "george"}], Post, :create) diff --git a/test/support/resources/post.ex b/test/support/resources/post.ex index b31531c0..33df353c 100644 --- a/test/support/resources/post.ex +++ b/test/support/resources/post.ex @@ -174,6 +174,7 @@ defmodule AshPostgres.Test.Post do source(:title_column) end + attribute(:datetime, :utc_datetime_usec, public?: true) attribute(:score, :integer, public?: true) attribute(:public, :boolean, public?: true) attribute(:category, :ci_string, public?: true)