Current section
Files
Jump to
Current section
Files
src/queryb.erl
-module(queryb).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([to_sql/3, to_postgresql/1]).
-export_type([query_error/0, 'query'/0, value/0, query_value/0, condition/0, case_sensitivity/0, direction/0]).
-type query_error() :: {constraint_violation, binary(), binary()} |
{database_error, binary()}.
-type 'query'() :: {select,
binary(),
list({gleam@option:option(binary()), binary()}),
{binary(), direction()},
integer(),
integer(),
list(condition())} |
{update, binary(), list({binary(), query_value()}), list(condition())} |
{delete, binary(), list(condition())}.
-type value() :: {text_value, binary()} |
{int_value, integer()} |
{float_value, float()} |
null_value.
-type query_value() :: {relation_value, gleam@option:option(binary()), binary()} |
{parameter, integer()}.
-type condition() :: {equal, query_value(), query_value()} |
{string_contains, query_value(), query_value(), case_sensitivity()} |
{'or', list(condition())}.
-type case_sensitivity() :: case_sensitive | case_insensitive.
-type direction() :: ascending | descending.
-spec escape_name(binary(), binary()) -> binary().
escape_name(Name, Quote) ->
<<<<"\""/utf8, (gleam@string:replace(Name, <<"\""/utf8>>, Quote))/binary>>/binary,
"\""/utf8>>.
-spec value_to_sql(binary(), query_value(), binary(), binary()) -> binary().
value_to_sql(Query, Value, Param, Quote) ->
case Value of
{relation_value, none, Column} ->
<<Query/binary, (escape_name(Column, Quote))/binary>>;
{relation_value, {some, Table}, Column@1} ->
<<<<<<Query/binary, (escape_name(Table, Quote))/binary>>/binary,
"."/utf8>>/binary,
(escape_name(Column@1, Quote))/binary>>;
{parameter, Index} ->
<<<<Query/binary, Param/binary>>/binary,
(gleam@int:to_string(Index))/binary>>
end.
-spec updates_to_sql(
binary(),
list({binary(), query_value()}),
boolean(),
binary(),
binary()
) -> binary().
updates_to_sql(Query, Updates, First, Param, Quote) ->
case Updates of
[] ->
Query;
[{Name, Value} | Updates@1] ->
Query@1 = case First of
true ->
<<Query/binary, " set "/utf8>>;
false ->
<<Query/binary, ", "/utf8>>
end,
Query@2 = <<<<Query@1/binary, (escape_name(Name, Quote))/binary>>/binary,
" = "/utf8>>,
Query@3 = value_to_sql(Query@2, Value, Param, Quote),
updates_to_sql(Query@3, Updates@1, false, Param, Quote)
end.
-spec condition_to_sql(binary(), condition(), binary(), binary()) -> binary().
condition_to_sql(Query, Condition, Param, Quote) ->
case Condition of
{'or', Conditions} ->
gleam@list:index_fold(
Conditions,
Query,
fun(Query@1, Condition@1, I) ->
Query@2 = case I of
0 ->
Query@1;
_ ->
<<Query@1/binary, " or "/utf8>>
end,
<<(condition_to_sql(
<<Query@2/binary, "("/utf8>>,
Condition@1,
Param,
Quote
))/binary,
")"/utf8>>
end
);
{equal, Left, Right} ->
Query@3 = <<(value_to_sql(Query, Left, Param, Quote))/binary,
" = "/utf8>>,
value_to_sql(Query@3, Right, Param, Quote);
{string_contains, Left@1, Right@1, Sensitivity} ->
Query@4 = value_to_sql(Query, Left@1, Param, Quote),
Query@5 = case Sensitivity of
case_sensitive ->
<<Query@4/binary, " like '%'||"/utf8>>;
case_insensitive ->
<<Query@4/binary, " ilike '%'||"/utf8>>
end,
<<(value_to_sql(Query@5, Right@1, Param, Quote))/binary,
"||'%'"/utf8>>
end.
-spec conditions_to_sql(
binary(),
list(condition()),
boolean(),
binary(),
binary()
) -> binary().
conditions_to_sql(Query, Conditions, First, Param, Quote) ->
case Conditions of
[] ->
Query;
[Condition | Conditions@1] ->
Query@1 = case First of
true ->
<<Query/binary, " where "/utf8>>;
false ->
<<Query/binary, " and "/utf8>>
end,
Query@2 = condition_to_sql(Query@1, Condition, Param, Quote),
conditions_to_sql(Query@2, Conditions@1, false, Param, Quote)
end.
-spec select_column(binary(), gleam@option:option(binary()), binary(), binary()) -> binary().
select_column(Query, Table, Column, Quote) ->
case Table of
none ->
<<Query/binary, (escape_name(Column, Quote))/binary>>;
{some, Table@1} ->
<<<<<<Query/binary, (escape_name(Table@1, Quote))/binary>>/binary,
"."/utf8>>/binary,
(escape_name(Column, Quote))/binary>>
end.
-spec select_columns(
binary(),
list({gleam@option:option(binary()), binary()}),
boolean(),
binary()
) -> binary().
select_columns(Query, Names, First, Quote) ->
case Names of
[] ->
Query;
[{Table, Column} | Names@1] when First ->
Query@1 = select_column(Query, Table, Column, Quote),
select_columns(Query@1, Names@1, false, Quote);
[{Table@1, Column@1} | Names@2] ->
Query@2 = select_column(
<<Query/binary, ", "/utf8>>,
Table@1,
Column@1,
Quote
),
select_columns(Query@2, Names@2, false, Quote)
end.
-spec to_sql('query'(), binary(), binary()) -> binary().
to_sql(Query, Param, Quote) ->
case Query of
{delete, Table, Conditions} ->
Query@1 = <<"delete from "/utf8,
(escape_name(Table, Quote))/binary>>,
conditions_to_sql(Query@1, Conditions, true, Param, Quote);
{update, Table@1, Updates, Conditions@1} ->
Query@2 = <<"update "/utf8, (escape_name(Table@1, Quote))/binary>>,
Query@3 = updates_to_sql(Query@2, Updates, true, Param, Quote),
conditions_to_sql(Query@3, Conditions@1, true, Param, Quote);
{select,
Table@2,
Columns,
{Order_by_column, Direction},
Limit,
Offset,
Conditions@2} ->
Query@4 = select_columns(<<"select "/utf8>>, Columns, true, Quote),
Query@5 = <<<<Query@4/binary, " from "/utf8>>/binary,
(escape_name(Table@2, Quote))/binary>>,
Query@6 = conditions_to_sql(
Query@5,
Conditions@2,
true,
Param,
Quote
),
Query@7 = <<<<Query@6/binary, " order by "/utf8>>/binary,
(escape_name(Order_by_column, Quote))/binary>>,
Query@8 = case Direction of
ascending ->
<<Query@7/binary, " asc"/utf8>>;
descending ->
<<Query@7/binary, " desc"/utf8>>
end,
Query@9 = <<<<Query@8/binary, " limit "/utf8>>/binary,
(gleam@int:to_string(Limit))/binary>>,
case Offset of
0 ->
Query@9;
_ ->
<<<<Query@9/binary, " offset "/utf8>>/binary,
(gleam@int:to_string(Offset))/binary>>
end
end.
-spec to_postgresql('query'()) -> binary().
to_postgresql(Query) ->
to_sql(Query, <<"$"/utf8>>, <<"\""/utf8>>).