Packages

SQL query builder core for Gleam

Current section

Files

Jump to
galchemy src galchemy@schema@relation.erl
Raw

src/galchemy@schema@relation.erl

-module(galchemy@schema@relation).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src\\galchemy\\schema\\relation.gleam").
-export([table_ref/2, pair/2, belongs_to/4, has_many/4, infer/1, for_table/3]).
-export_type([table_ref/0, relation_kind/0, column_pair/0, relation/0, table_relations/0]).
-type table_ref() :: {table_ref, binary(), binary()}.
-type relation_kind() :: belongs_to | has_many.
-type column_pair() :: {column_pair, binary(), binary()}.
-type relation() :: {relation,
binary(),
binary(),
relation_kind(),
table_ref(),
list(column_pair())}.
-type table_relations() :: {table_relations, table_ref(), list(relation())}.
-file("src\\galchemy\\schema\\relation.gleam", 34).
-spec table_ref(binary(), binary()) -> table_ref().
table_ref(Schema, Name) ->
{table_ref, Schema, Name}.
-file("src\\galchemy\\schema\\relation.gleam", 38).
-spec pair(binary(), binary()) -> column_pair().
pair(Local_column, Related_column) ->
{column_pair, Local_column, Related_column}.
-file("src\\galchemy\\schema\\relation.gleam", 42).
-spec belongs_to(binary(), binary(), table_ref(), list(column_pair())) -> relation().
belongs_to(Name, Foreign_key_name, Related_table, Column_pairs) ->
{relation, Name, Foreign_key_name, belongs_to, Related_table, Column_pairs}.
-file("src\\galchemy\\schema\\relation.gleam", 57).
-spec has_many(binary(), binary(), table_ref(), list(column_pair())) -> relation().
has_many(Name, Foreign_key_name, Related_table, Column_pairs) ->
{relation, Name, Foreign_key_name, has_many, Related_table, Column_pairs}.
-file("src\\galchemy\\schema\\relation.gleam", 130).
-spec add_relation(list(table_relations()), table_ref(), relation()) -> list(table_relations()).
add_relation(Relations_by_table, Target_table, Next_relation) ->
gleam@list:map(
Relations_by_table,
fun(Table_relations) ->
case erlang:element(2, Table_relations) =:= Target_table of
true ->
{table_relations,
erlang:element(2, Table_relations),
lists:append(
erlang:element(3, Table_relations),
[Next_relation]
)};
false ->
Table_relations
end
end
).
-file("src\\galchemy\\schema\\relation.gleam", 159).
-spec find_table_relations(list(table_relations()), table_ref()) -> gleam@option:option(table_relations()).
find_table_relations(Relations_by_table, Target_table) ->
case Relations_by_table of
[] ->
none;
[Table_relations | Rest] ->
case erlang:element(2, Table_relations) =:= Target_table of
true ->
{some, Table_relations};
false ->
find_table_relations(Rest, Target_table)
end
end.
-file("src\\galchemy\\schema\\relation.gleam", 148).
-spec add_relation_if_present(list(table_relations()), table_ref(), relation()) -> list(table_relations()).
add_relation_if_present(Relations_by_table, Target_table, Next_relation) ->
case find_table_relations(Relations_by_table, Target_table) of
{some, _} ->
add_relation(Relations_by_table, Target_table, Next_relation);
none ->
Relations_by_table
end.
-file("src\\galchemy\\schema\\relation.gleam", 200).
-spec dedupe_relation_name(binary(), list(binary()), integer()) -> binary().
dedupe_relation_name(Base_name, Used_names, Suffix) ->
Candidate = <<<<Base_name/binary, "_"/utf8>>/binary,
(erlang:integer_to_binary(Suffix))/binary>>,
case gleam@list:contains(Used_names, Candidate) of
true ->
dedupe_relation_name(Base_name, Used_names, Suffix + 1);
false ->
Candidate
end.
-file("src\\galchemy\\schema\\relation.gleam", 193).
-spec next_relation_name(binary(), list(binary())) -> binary().
next_relation_name(Base_name, Used_names) ->
case gleam@list:contains(Used_names, Base_name) of
true ->
dedupe_relation_name(Base_name, Used_names, 2);
false ->
Base_name
end.
-file("src\\galchemy\\schema\\relation.gleam", 174).
-spec normalize_relation_names(table_relations()) -> table_relations().
normalize_relation_names(Table_relations) ->
{Reversed_relations, _} = gleam@list:fold(
erlang:element(3, Table_relations),
{[], []},
fun(Acc, Relation) ->
{Normalized, Used_names} = Acc,
Normalized_name = next_relation_name(
erlang:element(2, Relation),
Used_names
),
{[{relation,
Normalized_name,
erlang:element(3, Relation),
erlang:element(4, Relation),
erlang:element(5, Relation),
erlang:element(6, Relation)} |
Normalized],
[Normalized_name | Used_names]}
end
),
{table_relations,
erlang:element(2, Table_relations),
lists:reverse(Reversed_relations)}.
-file("src\\galchemy\\schema\\relation.gleam", 269).
-spec zip_columns(list(binary()), list(binary())) -> list(column_pair()).
zip_columns(Local_columns, Related_columns) ->
gleam@list:map2(
Local_columns,
Related_columns,
fun(Local, Related) -> pair(Local, Related) end
).
-file("src\\galchemy\\schema\\relation.gleam", 296).
-spec is_identifier_character(binary()) -> boolean().
is_identifier_character(Character) ->
case Character of
<<"a"/utf8>> ->
true;
<<"b"/utf8>> ->
true;
<<"c"/utf8>> ->
true;
<<"d"/utf8>> ->
true;
<<"e"/utf8>> ->
true;
<<"f"/utf8>> ->
true;
<<"g"/utf8>> ->
true;
<<"h"/utf8>> ->
true;
<<"i"/utf8>> ->
true;
<<"j"/utf8>> ->
true;
<<"k"/utf8>> ->
true;
<<"l"/utf8>> ->
true;
<<"m"/utf8>> ->
true;
<<"n"/utf8>> ->
true;
<<"o"/utf8>> ->
true;
<<"p"/utf8>> ->
true;
<<"q"/utf8>> ->
true;
<<"r"/utf8>> ->
true;
<<"s"/utf8>> ->
true;
<<"t"/utf8>> ->
true;
<<"u"/utf8>> ->
true;
<<"v"/utf8>> ->
true;
<<"w"/utf8>> ->
true;
<<"x"/utf8>> ->
true;
<<"y"/utf8>> ->
true;
<<"z"/utf8>> ->
true;
<<"0"/utf8>> ->
true;
<<"1"/utf8>> ->
true;
<<"2"/utf8>> ->
true;
<<"3"/utf8>> ->
true;
<<"4"/utf8>> ->
true;
<<"5"/utf8>> ->
true;
<<"6"/utf8>> ->
true;
<<"7"/utf8>> ->
true;
<<"8"/utf8>> ->
true;
<<"9"/utf8>> ->
true;
<<"_"/utf8>> ->
true;
_ ->
false
end.
-file("src\\galchemy\\schema\\relation.gleam", 339).
-spec collapse_underscores(binary()) -> binary().
collapse_underscores(Value) ->
case gleam_stdlib:contains_string(Value, <<"__"/utf8>>) of
true ->
_pipe = Value,
_pipe@1 = gleam@string:replace(_pipe, <<"__"/utf8>>, <<"_"/utf8>>),
collapse_underscores(_pipe@1);
false ->
Value
end.
-file("src\\galchemy\\schema\\relation.gleam", 356).
-spec trim_leading_underscores(binary()) -> binary().
trim_leading_underscores(Value) ->
case gleam_stdlib:string_starts_with(Value, <<"_"/utf8>>) of
true ->
_pipe = Value,
_pipe@1 = gleam@string:drop_start(_pipe, 1),
trim_leading_underscores(_pipe@1);
false ->
Value
end.
-file("src\\galchemy\\schema\\relation.gleam", 367).
-spec trim_trailing_underscores(binary()) -> binary().
trim_trailing_underscores(Value) ->
case gleam_stdlib:string_ends_with(Value, <<"_"/utf8>>) of
true ->
_pipe = Value,
_pipe@1 = gleam@string:drop_end(_pipe, 1),
trim_trailing_underscores(_pipe@1);
false ->
Value
end.
-file("src\\galchemy\\schema\\relation.gleam", 350).
-spec trim_edge_underscores(binary()) -> binary().
trim_edge_underscores(Value) ->
_pipe = Value,
_pipe@1 = trim_leading_underscores(_pipe),
trim_trailing_underscores(_pipe@1).
-file("src\\galchemy\\schema\\relation.gleam", 391).
-spec is_digit(binary()) -> boolean().
is_digit(Character) ->
case Character of
<<"0"/utf8>> ->
true;
<<"1"/utf8>> ->
true;
<<"2"/utf8>> ->
true;
<<"3"/utf8>> ->
true;
<<"4"/utf8>> ->
true;
<<"5"/utf8>> ->
true;
<<"6"/utf8>> ->
true;
<<"7"/utf8>> ->
true;
<<"8"/utf8>> ->
true;
<<"9"/utf8>> ->
true;
_ ->
false
end.
-file("src\\galchemy\\schema\\relation.gleam", 378).
-spec prefix_if_needed(binary()) -> binary().
prefix_if_needed(Value) ->
case gleam@string:to_graphemes(Value) of
[First | _] ->
case is_digit(First) of
true ->
<<"relation_"/utf8, Value/binary>>;
false ->
Value
end;
[] ->
<<"relation"/utf8>>
end.
-file("src\\galchemy\\schema\\relation.gleam", 278).
-spec sanitize_name(binary(), binary()) -> binary().
sanitize_name(Value, Fallback) ->
Characters = gleam@string:to_graphemes(string:lowercase(Value)),
Normalized = begin
_pipe = gleam@list:fold(
Characters,
<<""/utf8>>,
fun(Acc, Character) -> case is_identifier_character(Character) of
true ->
<<Acc/binary, Character/binary>>;
false ->
<<Acc/binary, "_"/utf8>>
end end
),
_pipe@1 = collapse_underscores(_pipe),
trim_edge_underscores(_pipe@1)
end,
case Normalized of
<<""/utf8>> ->
sanitize_name(Fallback, <<"relation"/utf8>>);
_ ->
prefix_if_needed(Normalized)
end.
-file("src\\galchemy\\schema\\relation.gleam", 252).
-spec singularize_table_name(binary(), binary()) -> binary().
singularize_table_name(Table_name, Fallback_table_name) ->
Singular = case gleam_stdlib:string_ends_with(Table_name, <<"ies"/utf8>>) of
true ->
<<(gleam@string:drop_end(Table_name, 3))/binary, "y"/utf8>>;
false ->
case gleam_stdlib:string_ends_with(Table_name, <<"s"/utf8>>) of
true ->
gleam@string:drop_end(Table_name, 1);
false ->
Table_name
end
end,
sanitize_name(Singular, Fallback_table_name).
-file("src\\galchemy\\schema\\relation.gleam", 228).
-spec column_name_to_relation_name(binary(), binary()) -> binary().
column_name_to_relation_name(Column_name, Fallback_table_name) ->
case gleam_stdlib:string_ends_with(Column_name, <<"_id"/utf8>>) of
true ->
_pipe = Column_name,
_pipe@1 = gleam@string:drop_end(_pipe, 3),
sanitize_name(_pipe@1, Fallback_table_name);
false ->
case gleam_stdlib:string_ends_with(Column_name, <<"_uuid"/utf8>>) of
true ->
_pipe@2 = Column_name,
_pipe@3 = gleam@string:drop_end(_pipe@2, 5),
sanitize_name(_pipe@3, Fallback_table_name);
false ->
singularize_table_name(
Fallback_table_name,
Fallback_table_name
)
end
end.
-file("src\\galchemy\\schema\\relation.gleam", 213).
-spec base_belongs_to_name(galchemy@schema@model:foreign_key(), binary()) -> binary().
base_belongs_to_name(Foreign_key, _) ->
case erlang:element(3, Foreign_key) of
[Column_name] ->
column_name_to_relation_name(
Column_name,
erlang:element(5, Foreign_key)
);
_ ->
singularize_table_name(
erlang:element(5, Foreign_key),
erlang:element(5, Foreign_key)
)
end.
-file("src\\galchemy\\schema\\relation.gleam", 97).
-spec infer_table_relations(
galchemy@schema@model:table_schema(),
list(table_relations())
) -> list(table_relations()).
infer_table_relations(Table_schema, Acc) ->
gleam@list:fold(
erlang:element(7, Table_schema),
Acc,
fun(Inner_acc, Foreign_key) ->
Source_table = table_ref(
erlang:element(2, Table_schema),
erlang:element(3, Table_schema)
),
Target_table = table_ref(
erlang:element(4, Foreign_key),
erlang:element(5, Foreign_key)
),
Outgoing_relation = belongs_to(
base_belongs_to_name(
Foreign_key,
erlang:element(3, Table_schema)
),
erlang:element(2, Foreign_key),
Target_table,
zip_columns(
erlang:element(3, Foreign_key),
erlang:element(6, Foreign_key)
)
),
Incoming_relation = has_many(
sanitize_name(
erlang:element(3, Table_schema),
<<"related"/utf8>>
),
erlang:element(2, Foreign_key),
Source_table,
zip_columns(
erlang:element(6, Foreign_key),
erlang:element(3, Foreign_key)
)
),
_pipe = Inner_acc,
_pipe@1 = add_relation(_pipe, Source_table, Outgoing_relation),
add_relation_if_present(_pipe@1, Target_table, Incoming_relation)
end
).
-file("src\\galchemy\\schema\\relation.gleam", 72).
-spec infer(galchemy@schema@model:schema_snapshot()) -> list(table_relations()).
infer(Snapshot) ->
Base_relations = gleam@list:map(
erlang:element(2, Snapshot),
fun(Table_schema) ->
{table_relations,
table_ref(
erlang:element(2, Table_schema),
erlang:element(3, Table_schema)
),
[]}
end
),
_pipe = gleam@list:fold(
erlang:element(2, Snapshot),
Base_relations,
fun(Acc, Table_schema@1) ->
infer_table_relations(Table_schema@1, Acc)
end
),
gleam@list:map(_pipe, fun normalize_relation_names/1).
-file("src\\galchemy\\schema\\relation.gleam", 89).
-spec for_table(galchemy@schema@model:schema_snapshot(), binary(), binary()) -> gleam@option:option(table_relations()).
for_table(Snapshot, Schema_name, Table_name) ->
find_table_relations(infer(Snapshot), table_ref(Schema_name, Table_name)).