Current section
Files
Jump to
Current section
Files
src/sqlode@internal@codegen@queries.erl
-module(sqlode@internal@codegen@queries).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/internal/codegen/queries.gleam").
-export([render/5]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-file("src/sqlode/internal/codegen/queries.gleam", 173).
?DOC(false).
-spec engine_placeholder_style_expr(sqlode@internal@model:engine()) -> binary().
engine_placeholder_style_expr(Engine) ->
case Engine of
postgre_s_q_l ->
<<"runtime.DollarNumbered"/utf8>>;
my_s_q_l ->
<<"runtime.QuestionPositional"/utf8>>;
s_q_lite ->
<<"runtime.QuestionNumbered"/utf8>>
end.
-file("src/sqlode/internal/codegen/queries.gleam", 181).
?DOC(false).
-spec render_query_function(
sqlode@internal@naming:naming_context(),
sqlode@internal@model:analyzed_query(),
boolean(),
binary()
) -> binary().
render_query_function(Naming_ctx, Query, Emit_sql_as_comment, Style_expr) ->
Has_params = not gleam@list:is_empty(erlang:element(3, Query)),
Comment = case Emit_sql_as_comment of
true ->
[<<"// SQL: "/utf8,
(erlang:element(5, erlang:element(2, Query)))/binary>>];
false ->
[]
end,
Return_type = case Has_params of
true ->
Params_type = <<(sqlode@internal@naming:to_pascal_case(
Naming_ctx,
erlang:element(2, erlang:element(2, Query))
))/binary,
"Params"/utf8>>,
<<<<"runtime.RawQuery(params."/utf8, Params_type/binary>>/binary,
")"/utf8>>;
false ->
<<"runtime.RawQuery(Nil)"/utf8>>
end,
Encode_line = case Has_params of
true ->
Values_fn = <<(erlang:element(3, erlang:element(2, Query)))/binary,
"_values"/utf8>>,
<<<<" encode: params."/utf8, Values_fn/binary>>/binary,
","/utf8>>;
false ->
<<" encode: fn(_) { [] },"/utf8>>
end,
Slice_params = gleam@list:filter(
erlang:element(3, Query),
fun(P) -> erlang:element(6, P) end
),
Slice_info_line = case Slice_params of
[] ->
<<" slice_info: fn(_) { [] },"/utf8>>;
_ ->
Entries = begin
_pipe = Slice_params,
_pipe@1 = gleam@list:map(
_pipe,
fun(P@1) ->
<<<<<<<<"#("/utf8,
(erlang:integer_to_binary(
erlang:element(2, P@1)
))/binary>>/binary,
", list.length(p."/utf8>>/binary,
(erlang:element(3, P@1))/binary>>/binary,
"))"/utf8>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
<<<<" slice_info: fn(p) { ["/utf8, Entries/binary>>/binary,
"] },"/utf8>>
end,
gleam@string:join(
lists:append(
[Comment,
[<<<<<<<<"pub fn "/utf8,
(erlang:element(3, erlang:element(2, Query)))/binary>>/binary,
"() -> "/utf8>>/binary,
Return_type/binary>>/binary,
" {"/utf8>>,
<<" runtime.RawQuery("/utf8>>,
<<<<" name: \""/utf8,
(sqlode@internal@codegen@common:escape_string(
erlang:element(2, erlang:element(2, Query))
))/binary>>/binary,
"\","/utf8>>,
<<<<" sql: \""/utf8,
(sqlode@internal@codegen@common:escape_string(
erlang:element(5, erlang:element(2, Query))
))/binary>>/binary,
"\","/utf8>>,
<<<<" command: runtime."/utf8,
(sqlode@internal@model:query_command_to_string(
erlang:element(4, erlang:element(2, Query))
))/binary>>/binary,
","/utf8>>,
<<<<" param_count: "/utf8,
(erlang:integer_to_binary(
erlang:element(7, erlang:element(2, Query))
))/binary>>/binary,
","/utf8>>,
<<<<" placeholder_style: "/utf8, Style_expr/binary>>/binary,
","/utf8>>,
Encode_line,
Slice_info_line,
<<" )"/utf8>>,
<<"}"/utf8>>]]
),
<<"\n"/utf8>>
).
-file("src/sqlode/internal/codegen/queries.gleam", 318).
?DOC(false).
-spec param_arg_type(
sqlode@internal@model:query_param(),
sqlode@internal@model:type_mapping()
) -> binary().
param_arg_type(Param, Type_mapping) ->
Base = sqlode@internal@codegen@common:qualified_field_type(
erlang:element(4, Param),
Type_mapping
),
case erlang:element(6, Param) of
true ->
<<<<"List("/utf8, Base/binary>>/binary, ")"/utf8>>;
false ->
case erlang:element(5, Param) of
true ->
<<<<"Option("/utf8, Base/binary>>/binary, ")"/utf8>>;
false ->
Base
end
end.
-file("src/sqlode/internal/codegen/queries.gleam", 258).
?DOC(false).
-spec render_prepare_function(
sqlode@internal@naming:naming_context(),
sqlode@internal@model:analyzed_query(),
sqlode@internal@model:type_mapping()
) -> binary().
render_prepare_function(Naming_ctx, Query, Type_mapping) ->
Type_name = <<(sqlode@internal@naming:to_pascal_case(
Naming_ctx,
erlang:element(2, erlang:element(2, Query))
))/binary,
"Params"/utf8>>,
Descriptor_call = <<(erlang:element(3, erlang:element(2, Query)))/binary,
"()"/utf8>>,
case erlang:element(3, Query) of
[] ->
gleam@string:join(
[<<<<"pub fn prepare_"/utf8,
(erlang:element(3, erlang:element(2, Query)))/binary>>/binary,
"() -> #(String, List(runtime.Value)) {"/utf8>>,
<<<<" runtime.prepare("/utf8, Descriptor_call/binary>>/binary,
", Nil)"/utf8>>,
<<"}"/utf8>>],
<<"\n"/utf8>>
);
Params ->
Arg_list = begin
_pipe = Params,
_pipe@1 = gleam@list:map(
_pipe,
fun(P) ->
<<<<<<<<(erlang:element(3, P))/binary, " "/utf8>>/binary,
(erlang:element(3, P))/binary>>/binary,
": "/utf8>>/binary,
(param_arg_type(P, Type_mapping))/binary>>
end
),
gleam@string:join(_pipe@1, <<", "/utf8>>)
end,
Constructor_args = begin
_pipe@2 = Params,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(P@1) ->
<<<<(erlang:element(3, P@1))/binary, ": "/utf8>>/binary,
(erlang:element(3, P@1))/binary>>
end
),
gleam@string:join(_pipe@3, <<", "/utf8>>)
end,
gleam@string:join(
[<<<<<<<<"pub fn prepare_"/utf8,
(erlang:element(3, erlang:element(2, Query)))/binary>>/binary,
"("/utf8>>/binary,
Arg_list/binary>>/binary,
") -> #(String, List(runtime.Value)) {"/utf8>>,
<<" runtime.prepare("/utf8>>,
<<<<" "/utf8, Descriptor_call/binary>>/binary, ","/utf8>>,
<<<<<<<<" params."/utf8, Type_name/binary>>/binary,
"("/utf8>>/binary,
Constructor_args/binary>>/binary,
"),"/utf8>>,
<<" )"/utf8>>,
<<"}"/utf8>>],
<<"\n"/utf8>>
)
end.
-file("src/sqlode/internal/codegen/queries.gleam", 333).
?DOC(false).
-spec has_result_columns(sqlode@internal@model:analyzed_query()) -> boolean().
has_result_columns(Query) ->
case erlang:element(4, erlang:element(2, Query)) of
one ->
not gleam@list:is_empty(erlang:element(4, Query));
many ->
not gleam@list:is_empty(erlang:element(4, Query));
_ ->
false
end.
-file("src/sqlode/internal/codegen/queries.gleam", 540).
?DOC(false).
-spec module_alias_for(binary()) -> binary().
module_alias_for(Module_path) ->
case gleam@string:split(Module_path, <<"/"/utf8>>) of
[] ->
Module_path;
Segments ->
case gleam@list:last(Segments) of
{ok, Last} ->
Last;
{error, _} ->
Module_path
end
end.
-file("src/sqlode/internal/codegen/queries.gleam", 530).
?DOC(false).
-spec qualified_decoder_hook_call(gleam@option:option(binary()), binary()) -> binary().
qualified_decoder_hook_call(Module, Fn_name) ->
case Module of
{some, Module_path} ->
<<<<(module_alias_for(Module_path))/binary, "."/utf8>>/binary,
Fn_name/binary>>;
none ->
Fn_name
end.
-file("src/sqlode/internal/codegen/queries.gleam", 483).
?DOC(false).
-spec render_raw_base_decoder(
sqlode@internal@model:engine(),
sqlode@internal@model:scalar_type(),
sqlode@internal@model:type_mapping()
) -> binary().
render_raw_base_decoder(Engine, Scalar_type, Type_mapping_mode) ->
case Scalar_type of
{enum_type, _} ->
sqlode@internal@codegen@common:render_enum_or_set_decoder(
Scalar_type,
fun(_capture) ->
sqlode@internal@type_mapping:scalar_type_to_decoder(
Engine,
_capture
)
end
);
{set_type, _} ->
sqlode@internal@codegen@common:render_enum_or_set_decoder(
Scalar_type,
fun(_capture) ->
sqlode@internal@type_mapping:scalar_type_to_decoder(
Engine,
_capture
)
end
);
{custom_type, _, Module, _, {some, Hooks}} ->
<<<<<<<<"decode.map("/utf8,
(sqlode@internal@type_mapping:scalar_type_to_decoder(
Engine,
Scalar_type
))/binary>>/binary,
", "/utf8>>/binary,
(qualified_decoder_hook_call(
Module,
erlang:element(3, Hooks)
))/binary>>/binary,
")"/utf8>>;
_ ->
case (Type_mapping_mode =:= strong_mapping) andalso sqlode@internal@type_mapping:is_rich_type(
Scalar_type
) of
true ->
Constructor = sqlode@internal@type_mapping:scalar_type_to_gleam_type(
Scalar_type,
strong_mapping
),
<<<<<<<<"decode.map("/utf8,
(sqlode@internal@type_mapping:scalar_type_to_decoder(
Engine,
Scalar_type
))/binary>>/binary,
", models."/utf8>>/binary,
Constructor/binary>>/binary,
")"/utf8>>;
false ->
sqlode@internal@type_mapping:scalar_type_to_decoder(
Engine,
Scalar_type
)
end
end.
-file("src/sqlode/internal/codegen/queries.gleam", 340).
?DOC(false).
-spec render_decoder_function(
sqlode@internal@naming:naming_context(),
sqlode@internal@model:analyzed_query(),
sqlode@internal@model:engine(),
binary(),
gleam@dict:dict(binary(), binary()),
boolean(),
sqlode@internal@model:type_mapping()
) -> binary().
render_decoder_function(
Naming_ctx,
Query,
Engine,
_,
Table_matches,
Emit_exact_table_names,
Type_mapping_mode
) ->
Type_name = <<(sqlode@internal@naming:to_pascal_case(
Naming_ctx,
erlang:element(2, erlang:element(2, Query))
))/binary,
"Row"/utf8>>,
Constructor_name = case gleam_stdlib:map_get(
Table_matches,
erlang:element(3, erlang:element(2, Query))
) of
{ok, Table_type} ->
Table_type;
{error, _} ->
Type_name
end,
{_, Field_lines} = gleam@list:fold(
erlang:element(4, Query),
{0, []},
fun(Acc, Col) ->
{Idx, Lines} = Acc,
case Col of
{scalar_result, {result_column, Name, Scalar_type, Nullable, _}} ->
Field_name = sqlode@internal@naming:to_snake_case(
Naming_ctx,
Name
),
Base = render_raw_base_decoder(
Engine,
Scalar_type,
Type_mapping_mode
),
Decoder = case Nullable of
true ->
<<<<"decode.optional("/utf8, Base/binary>>/binary,
")"/utf8>>;
false ->
Base
end,
Line = <<<<<<<<<<<<" use "/utf8, Field_name/binary>>/binary,
" <- decode.field("/utf8>>/binary,
(erlang:integer_to_binary(Idx))/binary>>/binary,
", "/utf8>>/binary,
Decoder/binary>>/binary,
")"/utf8>>,
{Idx + 1, lists:append(Lines, [Line])};
{embedded_result,
{embedded_column, Embed_name, Embed_table_name, Embed_cols}} ->
Embed_field_name = sqlode@internal@naming:to_snake_case(
Naming_ctx,
Embed_name
),
Embed_type_name = sqlode@internal@naming:table_type_name(
Naming_ctx,
Embed_table_name,
Emit_exact_table_names
),
{New_idx, Decode_lines} = gleam@list:fold(
Embed_cols,
{Idx, []},
fun(Inner_acc, C) ->
{I, Ls} = Inner_acc,
Field_name@1 = sqlode@internal@naming:to_snake_case(
Naming_ctx,
erlang:element(2, C)
),
Base@1 = render_raw_base_decoder(
Engine,
erlang:element(3, C),
Type_mapping_mode
),
Decoder@1 = case erlang:element(4, C) of
true ->
<<<<"decode.optional("/utf8, Base@1/binary>>/binary,
")"/utf8>>;
false ->
Base@1
end,
Line@1 = <<<<<<<<<<<<" use "/utf8,
Field_name@1/binary>>/binary,
" <- decode.field("/utf8>>/binary,
(erlang:integer_to_binary(I))/binary>>/binary,
", "/utf8>>/binary,
Decoder@1/binary>>/binary,
")"/utf8>>,
{I + 1, lists:append(Ls, [Line@1])}
end
),
Embed_constructor_fields = begin
_pipe = gleam@list:map(
Embed_cols,
fun(C@1) ->
<<(sqlode@internal@naming:to_snake_case(
Naming_ctx,
erlang:element(2, C@1)
))/binary,
":"/utf8>>
end
),
gleam@string:join(_pipe, <<", "/utf8>>)
end,
Constructor_line = <<<<<<<<<<<<" let "/utf8,
Embed_field_name/binary>>/binary,
" = models."/utf8>>/binary,
Embed_type_name/binary>>/binary,
"("/utf8>>/binary,
Embed_constructor_fields/binary>>/binary,
")"/utf8>>,
All_lines = lists:append(Decode_lines, [Constructor_line]),
{New_idx, lists:append(Lines, All_lines)}
end
end
),
Constructor_fields = begin
_pipe@1 = erlang:element(4, Query),
_pipe@2 = gleam@list:flat_map(_pipe@1, fun(Col@1) -> case Col@1 of
{scalar_result, {result_column, Name@1, _, _, _}} ->
[<<(sqlode@internal@naming:to_snake_case(
Naming_ctx,
Name@1
))/binary,
":"/utf8>>];
{embedded_result, {embedded_column, Name@2, _, _}} ->
[<<(sqlode@internal@naming:to_snake_case(
Naming_ctx,
Name@2
))/binary,
":"/utf8>>]
end end),
gleam@string:join(_pipe@2, <<", "/utf8>>)
end,
gleam@string:join(
lists:append(
[[<<<<<<<<"pub fn "/utf8,
(erlang:element(3, erlang:element(2, Query)))/binary>>/binary,
"_decoder() -> decode.Decoder(models."/utf8>>/binary,
Type_name/binary>>/binary,
") {"/utf8>>],
Field_lines,
[<<<<<<<<" decode.success(models."/utf8,
Constructor_name/binary>>/binary,
"("/utf8>>/binary,
Constructor_fields/binary>>/binary,
"))"/utf8>>,
<<"}"/utf8>>]]
),
<<"\n"/utf8>>
).
-file("src/sqlode/internal/codegen/queries.gleam", 571).
?DOC(false).
-spec resolve_static_placeholders(
binary(),
integer(),
sqlode@internal@model:engine()
) -> binary().
resolve_static_placeholders(Sql, Param_count, Engine) ->
gleam@int:range(
1,
Param_count + 1,
Sql,
fun(Current_sql, Idx) ->
Param = sqlode@runtime:param_marker(Idx),
Slice = sqlode@runtime:slice_marker(Idx),
Placeholder = case Engine of
postgre_s_q_l ->
<<"$"/utf8, (erlang:integer_to_binary(Idx))/binary>>;
s_q_lite ->
<<"?"/utf8, (erlang:integer_to_binary(Idx))/binary>>;
my_s_q_l ->
<<"?"/utf8>>
end,
_pipe = Current_sql,
_pipe@1 = gleam@string:replace(_pipe, Param, Placeholder),
gleam@string:replace(_pipe@1, Slice, Placeholder)
end
).
-file("src/sqlode/internal/codegen/queries.gleam", 551).
?DOC(false).
-spec render_query_info_literal(
sqlode@internal@model:parsed_query(),
sqlode@internal@model:engine()
) -> binary().
render_query_info_literal(Query, Engine) ->
Sql = resolve_static_placeholders(
erlang:element(5, Query),
erlang:element(7, Query),
Engine
),
<<<<<<<<<<<<<<<<"runtime.QueryInfo(name: \""/utf8,
(sqlode@internal@codegen@common:escape_string(
erlang:element(2, Query)
))/binary>>/binary,
"\", sql: \""/utf8>>/binary,
(sqlode@internal@codegen@common:escape_string(Sql))/binary>>/binary,
"\", command: runtime."/utf8>>/binary,
(sqlode@internal@model:query_command_to_string(
erlang:element(4, Query)
))/binary>>/binary,
", param_count: "/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(7, Query)))/binary>>/binary,
")"/utf8>>.
-file("src/sqlode/internal/codegen/queries.gleam", 12).
?DOC(false).
-spec render(
sqlode@internal@naming:naming_context(),
sqlode@internal@model:sql_block(),
list(sqlode@internal@model:analyzed_query()),
gleam@dict:dict(binary(), binary()),
boolean()
) -> binary().
render(Naming_ctx, Block, Queries, Table_matches, Emit_exact_table_names) ->
{sql_block, _, Engine, _, _, Gleam, _} = Block,
{gleam_output, Out, Runtime, _, _, _, _, _, _, _} = Gleam,
Module_path = sqlode@internal@codegen@common:out_to_module_path(Out),
Emit_sql_as_comment = erlang:element(5, Gleam),
Has_slices = sqlode@internal@codegen@common:queries_have_slices(Queries),
Style_expr = engine_placeholder_style_expr(Engine),
Query_functions = begin
_pipe = Queries,
_pipe@1 = gleam@list:map(
_pipe,
fun(_capture) ->
render_query_function(
Naming_ctx,
_capture,
Emit_sql_as_comment,
Style_expr
)
end
),
gleam@string:join(_pipe@1, <<"\n\n"/utf8>>)
end,
Prepare_functions = begin
_pipe@2 = Queries,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(_capture@1) ->
render_prepare_function(
Naming_ctx,
_capture@1,
erlang:element(4, Gleam)
)
end
),
_pipe@4 = gleam@list:filter(_pipe@3, fun(S) -> S /= <<""/utf8>> end),
gleam@string:join(_pipe@4, <<"\n\n"/utf8>>)
end,
All_items = case Queries of
[] ->
<<"[]"/utf8>>;
_ ->
_pipe@5 = Queries,
_pipe@6 = gleam@list:map(
_pipe@5,
fun(Query) ->
render_query_info_literal(erlang:element(2, Query), Engine)
end
),
_pipe@7 = gleam@string:join(_pipe@6, <<", "/utf8>>),
(fun(Items) -> <<<<"["/utf8, Items/binary>>/binary, "]"/utf8>> end)(
_pipe@7
)
end,
Has_any_params = gleam@list:any(
Queries,
fun(Q) -> not gleam@list:is_empty(erlang:element(3, Q)) end
),
Decoder_queries = begin
_pipe@8 = Queries,
gleam@list:filter(_pipe@8, fun(Q@1) -> has_result_columns(Q@1) end)
end,
Decoder_functions = case Decoder_queries of
[] ->
<<""/utf8>>;
_ ->
_pipe@9 = Decoder_queries,
_pipe@10 = gleam@list:map(
_pipe@9,
fun(_capture@2) ->
render_decoder_function(
Naming_ctx,
_capture@2,
Engine,
Module_path,
Table_matches,
Emit_exact_table_names,
erlang:element(4, Gleam)
)
end
),
gleam@string:join(_pipe@10, <<"\n\n"/utf8>>)
end,
Has_decoders = not gleam@list:is_empty(Decoder_queries),
Has_nullable = (Has_decoders andalso gleam@list:any(
Decoder_queries,
fun(Q@2) ->
gleam@list:any(erlang:element(4, Q@2), fun(Col) -> case Col of
{scalar_result, {result_column, _, _, true, _}} ->
true;
_ ->
false
end end)
end
))
orelse gleam@list:any(
Queries,
fun(Q@3) ->
gleam@list:any(
erlang:element(3, Q@3),
fun(P) -> erlang:element(5, P) end
)
end
),
Needs_models_for_rich_params = ((erlang:element(4, Gleam) =:= strong_mapping)
orelse (erlang:element(4, Gleam) =:= rich_mapping))
andalso gleam@list:any(
Queries,
fun(Q@4) ->
gleam@list:any(
erlang:element(3, Q@4),
fun(P@1) ->
sqlode@internal@type_mapping:is_rich_type(
erlang:element(4, P@1)
)
end
)
end
),
Custom_imports = begin
_pipe@11 = sqlode@internal@codegen@common:param_scalar_types(Queries),
sqlode@internal@codegen@common:custom_type_imports(_pipe@11)
end,
Imports = lists:append([case Has_slices of
true ->
[<<"import gleam/list"/utf8>>];
false ->
[]
end, case Has_decoders of
true ->
[<<"import gleam/dynamic/decode"/utf8>>];
false ->
[]
end, case Has_nullable of
true ->
[<<"import gleam/option.{type Option}"/utf8>>];
false ->
[]
end, [<<"import "/utf8,
(sqlode@internal@codegen@common:runtime_import_path(Gleam))/binary>>], case Has_any_params of
true ->
[<<<<"import "/utf8, Module_path/binary>>/binary,
"/params"/utf8>>];
false ->
[]
end, case Has_decoders orelse Needs_models_for_rich_params of
true ->
[<<<<"import "/utf8, Module_path/binary>>/binary,
"/models"/utf8>>];
false ->
[]
end, Custom_imports]),
gleam@string:join(
lists:append(
[[<<"// Code generated by sqlode. DO NOT EDIT."/utf8>>,
<<"// engine: "/utf8,
(sqlode@internal@model:engine_to_string(Engine))/binary>>,
<<"// runtime: "/utf8,
(sqlode@internal@model:runtime_to_string(Runtime))/binary>>,
<<""/utf8>>],
Imports,
[<<""/utf8>>,
<<"pub fn all() -> List(runtime.QueryInfo) {"/utf8>>,
<<" "/utf8, All_items/binary>>,
<<"}"/utf8>>,
<<""/utf8>>,
Query_functions],
case Prepare_functions of
<<""/utf8>> ->
[];
_ ->
[<<""/utf8>>, Prepare_functions]
end,
case Decoder_functions of
<<""/utf8>> ->
[];
_ ->
[<<""/utf8>>, Decoder_functions]
end]
),
<<"\n"/utf8>>
).