Current section
Files
Jump to
Current section
Files
src/sqlode@query_analyzer@placeholder.erl
-module(sqlode@query_analyzer@placeholder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/query_analyzer/placeholder.gleam").
-export([unique/1, placeholder_index_for_token/3, sequential_placeholder/1, resolve_index/4, is_placeholder_token/1, extract/3]).
-export_type([placeholder_occurrence/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type placeholder_occurrence() :: {placeholder_occurrence,
integer(),
binary(),
binary()}.
-file("src/sqlode/query_analyzer/placeholder.gleam", 26).
-spec unique(list(placeholder_occurrence())) -> list(placeholder_occurrence()).
unique(Occurrences) ->
{_, Result} = gleam@list:fold(
Occurrences,
{maps:new(), []},
fun(Acc, Occurrence) ->
{Seen, Items} = Acc,
case gleam@dict:has_key(Seen, erlang:element(2, Occurrence)) of
true ->
Acc;
false ->
{gleam@dict:insert(Seen, erlang:element(2, Occurrence), nil),
[Occurrence | Items]}
end
end
),
lists:reverse(Result).
-file("src/sqlode/query_analyzer/placeholder.gleam", 71).
-spec marker_index(binary()) -> gleam@option:option(integer()).
marker_index(Token) ->
Rest = case gleam_stdlib:string_starts_with(
Token,
<<"__sqlode_param_"/utf8>>
) of
true ->
{some, gleam@string:drop_start(Token, 15)};
false ->
case gleam_stdlib:string_starts_with(
Token,
<<"__sqlode_slice_"/utf8>>
) of
true ->
{some, gleam@string:drop_start(Token, 15)};
false ->
none
end
end,
case Rest of
none ->
none;
{some, Body} ->
Core = case gleam_stdlib:string_ends_with(Body, <<"__"/utf8>>) of
true ->
gleam@string:drop_end(Body, 2);
false ->
Body
end,
_pipe = gleam_stdlib:parse_int(Core),
gleam@option:from_result(_pipe)
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 43).
-spec placeholder_index_for_token(sqlode@model:engine(), binary(), integer()) -> gleam@option:option(integer()).
placeholder_index_for_token(Engine, Token, Occurrence) ->
case marker_index(Token) of
{some, _} = Matched ->
Matched;
none ->
case Engine of
postgre_s_q_l ->
_pipe = Token,
_pipe@1 = gleam@string:replace(
_pipe,
<<"$"/utf8>>,
<<""/utf8>>
),
_pipe@2 = gleam_stdlib:parse_int(_pipe@1),
gleam@option:from_result(_pipe@2);
my_s_q_l ->
{some, Occurrence};
s_q_lite ->
case gleam_stdlib:string_starts_with(Token, <<"?"/utf8>>)
andalso (Token /= <<"?"/utf8>>) of
true ->
_pipe@3 = Token,
_pipe@4 = gleam@string:replace(
_pipe@3,
<<"?"/utf8>>,
<<""/utf8>>
),
_pipe@5 = gleam_stdlib:parse_int(_pipe@4),
gleam@option:from_result(_pipe@5);
false ->
{some, Occurrence}
end
end
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 126).
-spec sequential_placeholder(sqlode@model:engine()) -> boolean().
sequential_placeholder(Engine) ->
case Engine of
postgre_s_q_l ->
false;
my_s_q_l ->
true;
s_q_lite ->
true
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 96).
?DOC(
" Resolves placeholder index with SQLite named-placeholder deduplication.\n"
" Returns #(Option(index), next_occurrence, updated_seen_dict).\n"
" For SQLite named tokens (not bare ?), reuses the index of the first\n"
" occurrence so that the same placeholder always maps to the same index.\n"
).
-spec resolve_index(
sqlode@model:engine(),
binary(),
integer(),
gleam@dict:dict(binary(), integer())
) -> {gleam@option:option(integer()),
integer(),
gleam@dict:dict(binary(), integer())}.
resolve_index(Engine, Token, Occurrence, Seen) ->
case Engine of
s_q_lite when Token =/= <<"?"/utf8>> ->
case gleam_stdlib:map_get(Seen, Token) of
{ok, Existing_index} ->
{{some, Existing_index}, Occurrence, Seen};
{error, _} ->
Maybe_index = placeholder_index_for_token(
Engine,
Token,
Occurrence
),
Stored = case Maybe_index of
{some, I} ->
I;
none ->
Occurrence
end,
{Maybe_index,
Occurrence + 1,
gleam@dict:insert(Seen, Token, Stored)}
end;
_ ->
Next = case sequential_placeholder(Engine) of
true ->
Occurrence + 1;
false ->
Occurrence
end,
{placeholder_index_for_token(Engine, Token, Occurrence), Next, Seen}
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 133).
-spec is_placeholder_token(binary()) -> boolean().
is_placeholder_token(Value) ->
((gleam_stdlib:string_starts_with(Value, <<"$"/utf8>>) orelse gleam_stdlib:string_starts_with(
Value,
<<":"/utf8>>
))
orelse gleam_stdlib:string_starts_with(Value, <<"@"/utf8>>))
orelse gleam_stdlib:string_starts_with(Value, <<"?"/utf8>>).
-file("src/sqlode/query_analyzer/placeholder.gleam", 200).
-spec placeholder_tokens(sqlode@model:engine(), binary()) -> list(binary()).
placeholder_tokens(Engine, Sql) ->
_pipe = sqlode@lexer:tokenize(Sql, Engine),
sqlode@query_analyzer@token_utils:extract_placeholders(_pipe).
-file("src/sqlode/query_analyzer/placeholder.gleam", 212).
-spec named_placeholder_name(binary()) -> gleam@option:option(binary()).
named_placeholder_name(Token) ->
case Token of
<<"?"/utf8>> ->
none;
_ ->
case ((gleam_stdlib:string_starts_with(Token, <<"$"/utf8>>) orelse gleam_stdlib:string_starts_with(
Token,
<<":"/utf8>>
))
orelse gleam_stdlib:string_starts_with(Token, <<"@"/utf8>>))
orelse gleam_stdlib:string_starts_with(Token, <<"?"/utf8>>) of
true ->
Raw_name = gleam@string:slice(
Token,
1,
string:length(Token) - 1
),
case sqlode@char_utils:all_digits(Raw_name) of
true ->
none;
false ->
{some, Raw_name}
end;
false ->
none
end
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 205).
-spec default_param_name(
sqlode@query_analyzer@context:analyzer_context(),
binary(),
integer()
) -> binary().
default_param_name(Ctx, Token, Index) ->
case named_placeholder_name(Token) of
{some, Name} ->
sqlode@naming:to_snake_case(erlang:element(2, Ctx), Name);
none ->
<<"param"/utf8, (erlang:integer_to_binary(Index))/binary>>
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 140).
-spec build_occurrences(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
list(binary()),
integer(),
gleam@dict:dict(binary(), integer()),
list(placeholder_occurrence())
) -> list(placeholder_occurrence()).
build_occurrences(Ctx, Engine, Tokens, Occurrence, Seen, Acc) ->
case Tokens of
[] ->
lists:reverse(Acc);
[Token | Rest] ->
case Engine of
s_q_lite when Token =/= <<"?"/utf8>> ->
case gleam_stdlib:map_get(Seen, Token) of
{ok, Existing_index} ->
Default_name = default_param_name(
Ctx,
Token,
Existing_index
),
build_occurrences(
Ctx,
Engine,
Rest,
Occurrence,
Seen,
[{placeholder_occurrence,
Existing_index,
Token,
Default_name} |
Acc]
);
{error, _} ->
Index = case placeholder_index_for_token(
Engine,
Token,
Occurrence
) of
{some, Value} ->
Value;
none ->
Occurrence
end,
Default_name@1 = default_param_name(
Ctx,
Token,
Index
),
build_occurrences(
Ctx,
Engine,
Rest,
Occurrence + 1,
gleam@dict:insert(Seen, Token, Index),
[{placeholder_occurrence,
Index,
Token,
Default_name@1} |
Acc]
)
end;
_ ->
Index@1 = case placeholder_index_for_token(
Engine,
Token,
Occurrence
) of
{some, Value@1} ->
Value@1;
none ->
Occurrence
end,
Default_name@2 = default_param_name(Ctx, Token, Index@1),
build_occurrences(
Ctx,
Engine,
Rest,
Occurrence + 1,
Seen,
[{placeholder_occurrence,
Index@1,
Token,
Default_name@2} |
Acc]
)
end
end.
-file("src/sqlode/query_analyzer/placeholder.gleam", 17).
-spec extract(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
binary()
) -> list(placeholder_occurrence()).
extract(Ctx, Engine, Sql) ->
Tokens = placeholder_tokens(Engine, Sql),
build_occurrences(Ctx, Engine, Tokens, 1, maps:new(), []).