Packages

ash_postgres

1.5.15
2.10.0 2.9.1 2.9.0 2.8.0 2.7.0 2.6.32 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.22 2.5.21 2.5.20 2.5.19 2.5.18 2.5.17 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.1 2.3.0 2.2.5 2.2.4 2.2.3 2.2.2 2.2.1 2.2.0 2.1.19 2.1.18 2.1.17 2.1.15 2.1.14 2.1.13 2.1.12 2.1.11 2.1.10 2.1.9 2.1.8 2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.12 2.0.11 2.0.10 2.0.9 2.0.8 2.0.7 2.0.6 2.0.5 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 1.5.30 1.5.29 1.5.28 1.5.27 1.5.26 1.5.25 1.5.24 1.5.23 1.5.22 1.5.21 1.5.20 1.5.19 1.5.18 1.5.17 1.5.16 1.5.15 1.5.14 1.5.13 1.5.12 1.5.11 1.5.10 1.5.9 1.5.8 1.5.7 1.5.6 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 1.4.0 1.3.68 1.3.67 1.3.66 1.3.65 1.3.64 1.3.63 1.3.62 1.3.61 1.3.60 1.3.59 1.3.58 1.3.56 1.3.55 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 1.3.47 1.3.46 1.3.45 1.3.44 1.3.43 1.3.42 1.3.41 1.3.40 1.3.39 1.3.38 1.3.37 1.3.36 1.3.35 1.3.34 1.3.33 1.3.32 1.3.31 1.3.30 1.3.29 1.3.28 1.3.26 1.3.25 1.3.24 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 1.3.18 1.3.17 1.3.16 1.3.15 1.3.14 1.3.12 1.3.11 1.3.10 1.3.9 1.3.8 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.3.0-rc.4 1.3.0-rc.3 1.3.0-rc.2 1.3.0-rc.0 1.2.6 1.2.5 1.2.4 1.2.3 1.2.2 1.2.1 1.2.0 1.2.0-rc.1 1.2.0-rc.0 1.1.3 1.1.2 1.1.1 1.1.0 1.0.0 1.0.0-rc.9 1.0.0-rc.8 1.0.0-rc.7 1.0.0-rc.6 1.0.0-rc.5 1.0.0-rc.4 1.0.0-rc.3 1.0.0-rc.2 1.0.0-rc.1 1.0.0-rc.0 1.0.0-pre.3 1.0.0-pre.2 1.0.0-pre.1 1.0.0-pre.0 0.43.0 0.42.0-rc.7 0.42.0-rc.6 0.42.0-rc.5 0.42.0-rc.4 0.42.0-rc.3 0.42.0-rc.1 0.42.0-rc.0 0.41.7 0.41.6 0.41.5 0.41.4 0.41.3 0.41.2 0.41.1 0.41.0-rc0 retired 0.41.0-rc.9 0.41.0-rc.8 0.41.0-rc.7 0.41.0-rc.6 0.41.0-rc.5 0.41.0-rc.4 0.41.0-rc.3 0.41.0-rc.2 0.41.0-rc.1 0.41.0-rc.0 0.40.11 0.40.10 0.40.9 0.40.8 0.40.7 0.40.6 0.40.5 0.40.4 0.40.3 0.40.2 0.40.1 0.40.0-rc5 0.40.0-rc4 0.40.0-rc3 0.40.0-rc2 0.40.0-rc1 0.39.0-rc0 0.38.11 0.38.10 0.38.9 0.38.8 0.38.7 0.38.6 0.38.5 0.38.4 0.38.3 0.38.2 0.38.1 0.38.0 0.37.8 0.37.7 0.37.6 0.37.5 0.37.4 0.37.3 0.37.2 0.37.1 0.37.0 0.36.5 0.36.4 0.36.3 0.36.2 0.36.1 0.36.0 0.35.5 0.35.4 0.35.3 0.35.1 0.34.5 0.34.4 0.34.3 0.34.2 0.34.1 0.34.0 0.33.1 0.33.0 0.32.2 0.32.1 0.32.0 0.31.1 0.30.1 0.29.6 0.29.5 0.29.4 0.29.3 0.29.2 0.29.1 0.28.1 0.28.0 0.27.0 0.26.2 0.26.1 0.26.0 0.25.5 0.25.4 0.25.3 0.25.2 0.25.1 0.25.0 0.24.0 0.23.2 0.23.1 0.23.0 0.22.1 0.22.0 0.21.0 0.20.1 0.20.0 0.19.0 0.18.0 0.17.0 0.16.1 0.15.0 0.14.0 0.13.0 0.12.1 0.12.0 0.11.2 0.10.0 0.9.0 0.8.0 0.7.0 0.6.0 0.5.0 0.4.0 0.3.0 0.2.1 0.2.0 0.1.4 0.1.3 0.1.2 0.1.1 0.1.1-rc.2 0.1.1-rc.1 0.1.1-rc.0 0.1.0

The PostgreSQL data layer for Ash Framework

Current section

Files

Jump to
ash_postgres lib calculation.ex
Raw

lib/calculation.ex

defmodule AshPostgres.Calculation do
@moduledoc false
require Ecto.Query
@next_calculation_names Enum.reduce(0..999, %{}, fn i, acc ->
Map.put(acc, :"calculation_#{i}", :"calculation_#{i + 1}")
end)
def add_calculations(query, [], _, _, _select?), do: {:ok, query}
def add_calculations(query, calculations, resource, source_binding, select?) do
query = AshPostgres.DataLayer.default_bindings(query, resource)
{:ok, query} =
AshPostgres.Join.join_all_relationships(
query,
%Ash.Filter{
resource: resource,
expression: Enum.map(calculations, &elem(&1, 1))
},
left_only?: true
)
aggregates =
calculations
|> Enum.flat_map(fn {calculation, expression} ->
expression
|> Ash.Filter.used_aggregates([])
|> Enum.map(&Map.put(&1, :context, calculation.context))
end)
|> Enum.uniq()
{query, calculations} =
Enum.reduce(
calculations,
{query, []},
fn {calculation, expression}, {query, calculations} ->
if is_atom(calculation.name) do
{query, [{calculation, expression} | calculations]}
else
{query, name} = use_calculation_name(query, calculation.name)
{query, [{%{calculation | name: name}, expression} | calculations]}
end
end
)
case AshPostgres.Aggregate.add_aggregates(
query,
aggregates,
query.__ash_bindings__.resource,
false,
source_binding
) do
{:ok, query} ->
if select? do
query =
if query.select do
query
else
Ecto.Query.select_merge(query, %{})
end
{dynamics, query} =
Enum.reduce(calculations, {[], query}, fn {calculation, expression}, {list, query} ->
type =
AshPostgres.Types.parameterized_type(
calculation.type,
Map.get(calculation, :constraints, [])
)
expression =
Ash.Actions.Read.add_calc_context_to_filter(
expression,
calculation.context[:actor],
calculation.context[:authorize?],
calculation.context[:tenant],
calculation.context[:tracer]
)
{expr, acc} =
AshPostgres.Expr.dynamic_expr(
query,
expression,
query.__ash_bindings__,
false,
type
)
expr =
if type do
Ecto.Query.dynamic(type(^expr, ^type))
else
expr
end
{[{calculation.load, calculation.name, expr} | list],
AshPostgres.DataLayer.merge_expr_accumulator(query, acc)}
end)
{:ok, add_calculation_selects(query, dynamics)}
else
{:ok, query}
end
{:error, error} ->
{:error, error}
end
end
def next_calculation_name(i) do
@next_calculation_names[i] ||
raise Ash.Error.Framework.AssumptionFailed,
message: """
All 1000 static names for calculations have been used in a single query.
Congratulations, this means that you have gone so wildly beyond our imagination
of how much can fit into a single quer. Please file an issue and we will raise the limit.
"""
end
defp use_calculation_name(query, aggregate_name) do
{%{
query
| __ash_bindings__: %{
query.__ash_bindings__
| current_calculation_name:
next_calculation_name(query.__ash_bindings__.current_calculation_name),
calculation_names:
Map.put(
query.__ash_bindings__.calculation_names,
aggregate_name,
query.__ash_bindings__.current_calculation_name
)
}
}, query.__ash_bindings__.current_calculation_name}
end
defp add_calculation_selects(query, dynamics) do
{in_calculations, in_body} =
Enum.split_with(dynamics, fn {load, _name, _dynamic} -> is_nil(load) end)
calcs =
in_body
|> Map.new(fn {load, _, dynamic} ->
{load, dynamic}
end)
calcs =
if Enum.empty?(in_calculations) do
calcs
else
Map.put(
calcs,
:calculations,
Map.new(in_calculations, fn {_, name, dynamic} ->
{name, dynamic}
end)
)
end
Ecto.Query.select_merge(query, ^calcs)
end
end