From ab97b2947b4a5a5fff5017694e6e3424b85f6031 Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 15 Jul 2024 10:25:39 -0400 Subject: [PATCH] improvement: add `binding()` expression --- lib/data_layer.ex | 3 ++- lib/functions/binding.ex | 9 +++++++++ lib/sql_implementation.ex | 15 +++++++++++++++ test/calculation_test.exs | 14 ++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 lib/functions/binding.ex diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 0ed9f72..450acc8 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -785,7 +785,8 @@ defmodule AshPostgres.DataLayer do functions = [ AshPostgres.Functions.Like, - AshPostgres.Functions.ILike + AshPostgres.Functions.ILike, + AshPostgres.Functions.Binding ] functions = diff --git a/lib/functions/binding.ex b/lib/functions/binding.ex new file mode 100644 index 0000000..92fc9d6 --- /dev/null +++ b/lib/functions/binding.ex @@ -0,0 +1,9 @@ +defmodule AshPostgres.Functions.Binding do + @moduledoc """ + Refers to the current table binding. + """ + + use Ash.Query.Function, name: :binding + + def args, do: [[]] +end diff --git a/lib/sql_implementation.ex b/lib/sql_implementation.ex index aa0a7c7..90414cf 100644 --- a/lib/sql_implementation.ex +++ b/lib/sql_implementation.ex @@ -41,6 +41,21 @@ defmodule AshPostgres.SqlImplementation do {:ok, Ecto.Query.dynamic(fragment("'[]'::jsonb")), acc} end + def expr(query, %AshPostgres.Functions.Binding{}, _bindings, _embedded?, acc, _type) do + binding = AshSql.Bindings.get_binding( + query.__ash_bindings__.resource, + [], + query, + [:left, :inner, :root] + ) + + if is_nil(binding) do + raise "Error while constructing explicit `binding()` reference." + end + + {:ok, Ecto.Query.dynamic([{^binding, row}], row), acc} + end + def expr( query, %like{arguments: [arg1, arg2], embedded?: pred_embedded?}, diff --git a/test/calculation_test.exs b/test/calculation_test.exs index 186cf11..e15dbbc 100644 --- a/test/calculation_test.exs +++ b/test/calculation_test.exs @@ -800,6 +800,20 @@ defmodule AshPostgres.CalculationTest do |> Ash.read_one!() end + test "binding() can be used to refer to the current binding in a fragment" do + post = + Post + |> Ash.Changeset.for_create(:create, %{}) + |> Ash.create!() + + post_id = post.id + + assert [%{id: ^post_id}] = + Post + |> Ash.Query.filter(fragment("(?).id", binding()) == type(^post.id, :uuid)) + |> Ash.read!() + end + test "exists with a relationship that has a filtered read action works" do post = Post