-module(sqlode@internal@query_analyzer@placeholder). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/internal/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. ?MODULEDOC(false). -type placeholder_occurrence() :: {placeholder_occurrence, integer(), binary(), binary()}. -file("src/sqlode/internal/query_analyzer/placeholder.gleam", 26). ?DOC(false). -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/internal/query_analyzer/placeholder.gleam", 71). ?DOC(false). -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/internal/query_analyzer/placeholder.gleam", 43). ?DOC(false). -spec placeholder_index_for_token( sqlode@internal@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/internal/query_analyzer/placeholder.gleam", 126). ?DOC(false). -spec sequential_placeholder(sqlode@internal@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/internal/query_analyzer/placeholder.gleam", 96). ?DOC(false). -spec resolve_index( sqlode@internal@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/internal/query_analyzer/placeholder.gleam", 133). ?DOC(false). -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/internal/query_analyzer/placeholder.gleam", 207). ?DOC(false). -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@internal@char_utils:all_digits(Raw_name) of true -> none; false -> {some, Raw_name} end; false -> none end end. -file("src/sqlode/internal/query_analyzer/placeholder.gleam", 200). ?DOC(false). -spec default_param_name( sqlode@internal@query_analyzer@context:analyzer_context(), binary(), integer() ) -> binary(). default_param_name(Ctx, Token, Index) -> case named_placeholder_name(Token) of {some, Name} -> sqlode@internal@naming:to_snake_case(erlang:element(2, Ctx), Name); none -> <<"param"/utf8, (erlang:integer_to_binary(Index))/binary>> end. -file("src/sqlode/internal/query_analyzer/placeholder.gleam", 140). ?DOC(false). -spec build_occurrences( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@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/internal/query_analyzer/placeholder.gleam", 17). ?DOC(false). -spec extract( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@model:engine(), list(sqlode@internal@lexer:token()) ) -> list(placeholder_occurrence()). extract(Ctx, Engine, Tokens) -> Placeholder_tokens = sqlode@internal@query_analyzer@token_utils:extract_placeholders( Tokens ), build_occurrences(Ctx, Engine, Placeholder_tokens, 1, maps:new(), []).