Packages
ash_postgres
2.0.7
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
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/sql_implementation.ex
defmodule AshPostgres.SqlImplementation do
@moduledoc false
use AshSql.Implementation
require Ecto.Query
@impl true
def manual_relationship_function, do: :ash_postgres_join
@impl true
def manual_relationship_subquery_function, do: :ash_postgres_subquery
@impl true
def require_ash_functions_for_or_and_and?, do: true
@impl true
def require_extension_for_citext, do: {true, "citext"}
@impl true
def expr(
query,
%like{arguments: [arg1, arg2], embedded?: pred_embedded?},
bindings,
embedded?,
acc,
type
)
when like in [AshPostgres.Functions.Like, AshPostgres.Functions.ILike] do
{arg1, acc} =
AshSql.Expr.dynamic_expr(query, arg1, bindings, pred_embedded? || embedded?, :string, acc)
{arg2, acc} =
AshSql.Expr.dynamic_expr(query, arg2, bindings, pred_embedded? || embedded?, :string, acc)
inner_dyn =
if like == AshPostgres.Functions.Like do
Ecto.Query.dynamic(like(^arg1, ^arg2))
else
Ecto.Query.dynamic(ilike(^arg1, ^arg2))
end
if type != Ash.Type.Boolean do
{:ok, inner_dyn, acc}
else
{:ok, Ecto.Query.dynamic(type(^inner_dyn, ^type)), acc}
end
end
def expr(
query,
%AshPostgres.Functions.TrigramSimilarity{
arguments: [arg1, arg2],
embedded?: pred_embedded?
},
bindings,
embedded?,
acc,
_type
) do
{arg1, acc} =
AshSql.Expr.dynamic_expr(query, arg1, bindings, pred_embedded? || embedded?, :string, acc)
{arg2, acc} =
AshSql.Expr.dynamic_expr(query, arg2, bindings, pred_embedded? || embedded?, :string, acc)
{:ok, Ecto.Query.dynamic(fragment("similarity(?, ?)", ^arg1, ^arg2)), acc}
end
def expr(
query,
%AshPostgres.Functions.VectorCosineDistance{
arguments: [arg1, arg2],
embedded?: pred_embedded?
},
bindings,
embedded?,
acc,
_type
) do
{arg1, acc} =
AshSql.Expr.dynamic_expr(query, arg1, bindings, pred_embedded? || embedded?, :string, acc)
{arg2, acc} =
AshSql.Expr.dynamic_expr(query, arg2, bindings, pred_embedded? || embedded?, :string, acc)
{:ok, Ecto.Query.dynamic(fragment("(? <=> ?)", ^arg1, ^arg2)), acc}
end
def expr(
_query,
_expr,
_bindings,
_embedded?,
_acc,
_type
) do
:error
end
@impl true
def table(resource) do
AshPostgres.DataLayer.Info.table(resource)
end
@impl true
def schema(resource) do
AshPostgres.DataLayer.Info.schema(resource)
end
@impl true
def repo(resource, kind) do
AshPostgres.DataLayer.Info.repo(resource, kind)
end
@impl true
def simple_join_first_aggregates(resource) do
AshPostgres.DataLayer.Info.simple_join_first_aggregates(resource)
end
@impl true
def list_aggregate(resource) do
if AshPostgres.DataLayer.Info.pg_version_matches?(resource, ">= 16.0.0") do
"any_value"
else
"array_agg"
end
end
@impl true
def parameterized_type(type, constraints, no_maps? \\ true)
def parameterized_type({:parameterized, _} = type, _, _) do
type
end
def parameterized_type({:parameterized, _, _} = type, _, _) do
type
end
def parameterized_type({:in, type}, constraints, no_maps?) do
parameterized_type({:array, type}, constraints, no_maps?)
end
def parameterized_type({:array, type}, constraints, _) do
case parameterized_type(type, constraints[:items] || [], false) do
nil ->
nil
type ->
{:array, type}
end
end
def parameterized_type(Ash.Type.CiString, constraints, no_maps?) do
parameterized_type(AshPostgres.Type.CiStringWrapper, constraints, no_maps?)
end
def parameterized_type(Ash.Type.String, constraints, no_maps?) do
parameterized_type(AshPostgres.Type.StringWrapper, constraints, no_maps?)
end
def parameterized_type(:tsquery, constraints, no_maps?) do
parameterized_type(AshPostgres.Tsquery, constraints, no_maps?)
end
def parameterized_type(type, _constraints, false)
when type in [Ash.Type.Map, Ash.Type.Map.EctoType],
do: :map
def parameterized_type(type, _constraints, true)
when type in [Ash.Type.Map, Ash.Type.Map.EctoType],
do: nil
def parameterized_type(type, constraints, no_maps?) do
if Ash.Type.ash_type?(type) do
cast_in_query? =
if function_exported?(Ash.Type, :cast_in_query?, 2) do
Ash.Type.cast_in_query?(type, constraints)
else
Ash.Type.cast_in_query?(type)
end
if cast_in_query? do
type = Ash.Type.ecto_type(type)
parameterized_type(type, constraints, no_maps?)
else
nil
end
else
if is_atom(type) && :erlang.function_exported(type, :type, 1) do
type =
if type == :ci_string do
:citext
else
type
end
Ecto.ParameterizedType.init(type, constraints || [])
else
type
end
end
end
@impl true
def determine_types(mod, values) do
Code.ensure_compiled(mod)
name =
cond do
function_exported?(mod, :operator, 0) ->
mod.operator()
function_exported?(mod, :name, 0) ->
mod.name()
true ->
nil
end
cond do
:erlang.function_exported(mod, :types, 0) ->
mod.types()
:erlang.function_exported(mod, :args, 0) ->
mod.args()
true ->
[:any]
end
|> Enum.concat(Map.keys(Ash.Query.Operator.operator_overloads(name) || %{}))
|> Enum.map(fn types ->
case types do
:same ->
types =
for _ <- values do
:same
end
closest_fitting_type(types, values)
:any ->
for _ <- values do
:any
end
types ->
closest_fitting_type(types, values)
end
end)
|> Enum.filter(fn types ->
Enum.all?(types, &(vagueness(&1) == 0))
end)
|> case do
[type] ->
if type == :any || type == {:in, :any} do
nil
else
type
end
# There are things we could likely do here
# We only say "we know what types these are" when we explicitly know
_ ->
Enum.map(values, fn _ -> nil end)
end
end
defp closest_fitting_type(types, values) do
types_with_values = Enum.zip(types, values)
types_with_values
|> fill_in_known_types()
|> clarify_types()
end
defp clarify_types(types) do
basis =
types
|> Enum.map(&elem(&1, 0))
|> Enum.min_by(&vagueness(&1))
Enum.map(types, fn {type, _value} ->
replace_same(type, basis)
end)
end
defp replace_same({:in, type}, basis) do
{:in, replace_same(type, basis)}
end
defp replace_same(:same, :same) do
:any
end
defp replace_same(:same, {:in, :same}) do
{:in, :any}
end
defp replace_same(:same, basis) do
basis
end
defp replace_same(other, _basis) do
other
end
defp fill_in_known_types(types) do
Enum.map(types, &fill_in_known_type/1)
end
defp fill_in_known_type(
{{:array, type},
%Ash.Query.Function.Type{arguments: [inner, {:array, type}, constraints]} = func}
) do
{:in,
fill_in_known_type({type, %{func | arguments: [inner, type, constraints[:items] || []]}})}
end
defp fill_in_known_type(
{{:array, type},
%Ash.Query.Ref{attribute: %{type: {:array, type}, constraints: constraints} = attribute} =
ref}
) do
{:in,
fill_in_known_type(
{type,
%{ref | attribute: %{attribute | type: type, constraints: constraints[:items] || []}}}
)}
end
defp fill_in_known_type(
{vague_type, %Ash.Query.Function.Type{arguments: [_, type, constraints]}} = func
)
when vague_type in [:any, :same] do
if Ash.Type.ash_type?(type) do
type = type |> parameterized_type(constraints) |> array_to_in()
{type || :any, func}
else
type =
if is_atom(type) && :erlang.function_exported(type, :type, 1) do
Ecto.ParameterizedType.init(type, []) |> array_to_in()
else
type |> array_to_in()
end
{type, func}
end
end
defp fill_in_known_type(
{vague_type, %Ash.Query.Ref{attribute: %{type: type, constraints: constraints}}} = ref
)
when vague_type in [:any, :same] do
if Ash.Type.ash_type?(type) do
type = type |> parameterized_type(constraints) |> array_to_in()
{type || :any, ref}
else
type =
if is_atom(type) && :erlang.function_exported(type, :type, 1) do
Ecto.ParameterizedType.init(type, []) |> array_to_in()
else
type |> array_to_in()
end
{type, ref}
end
end
defp fill_in_known_type({type, value}), do: {array_to_in(type), value}
defp array_to_in({:array, v}), do: {:in, array_to_in(v)}
defp array_to_in(v), do: v
defp vagueness({:in, type}), do: vagueness(type)
defp vagueness(:same), do: 2
defp vagueness(:any), do: 1
defp vagueness(_), do: 0
end