From 64d81ca5e6b061a1a2b5d04b9acfe1417ff9aef7 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Tue, 19 May 2026 11:47:32 +0200 Subject: [PATCH 1/2] Ecto.Schema: Allow embedded_schema to have non-virtual :any fields --- lib/ecto/schema.ex | 5 ++++- test/ecto/schema_test.exs | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ecto/schema.ex b/lib/ecto/schema.ex index 4ccff13cc3..fa673ba2d7 100644 --- a/lib/ecto/schema.ex +++ b/lib/ecto/schema.ex @@ -1997,7 +1997,10 @@ defmodule Ecto.Schema do # better to raise unknown type first than unsupported option. type = check_field_type!(mod, name, type, opts) - if type == :any && !opts[:virtual] do + fields = Module.get_attribute(mod, :ecto_struct_fields) + schema_with_source? = match?(%Ecto.Schema.Metadata{}, fields[:__meta__]) + + if schema_with_source? && type == :any && !opts[:virtual] do raise ArgumentError, "only virtual fields can have type :any, " <> "invalid type for field #{inspect(name)}" diff --git a/test/ecto/schema_test.exs b/test/ecto/schema_test.exs index d84d78a78e..6c68168285 100644 --- a/test/ecto/schema_test.exs +++ b/test/ecto/schema_test.exs @@ -1185,6 +1185,15 @@ defmodule Ecto.SchemaTest do end end end + + # :any is allowed on embedded schema + defmodule EmbeddedFieldAny do + use Ecto.Schema + + embedded_schema do + field :json, :any + end + end end defmodule FieldAnyVirtual do From ca7dcbcae648ce3575ec9f376b6e2933faee0c1a Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Fri, 22 May 2026 10:21:57 +0200 Subject: [PATCH 2/2] Use `@ecto_source` --- lib/ecto/schema.ex | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/ecto/schema.ex b/lib/ecto/schema.ex index fa673ba2d7..de9d03d82c 100644 --- a/lib/ecto/schema.ex +++ b/lib/ecto/schema.ex @@ -1997,10 +1997,7 @@ defmodule Ecto.Schema do # better to raise unknown type first than unsupported option. type = check_field_type!(mod, name, type, opts) - fields = Module.get_attribute(mod, :ecto_struct_fields) - schema_with_source? = match?(%Ecto.Schema.Metadata{}, fields[:__meta__]) - - if schema_with_source? && type == :any && !opts[:virtual] do + if type == :any && !opts[:virtual] && Module.get_attribute(mod, :ecto_source) do raise ArgumentError, "only virtual fields can have type :any, " <> "invalid type for field #{inspect(name)}" @@ -2304,6 +2301,7 @@ defmodule Ecto.Schema do end Module.put_attribute(module, :ecto_schema_defined, line) + Module.put_attribute(module, :ecto_source, source) if Code.can_await_module_compilation?() do Module.put_attribute(module, :after_verify, Ecto.Schema)