-module(sqlode@internal@query_analyzer@param_inferencer). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/sqlode/internal/query_analyzer/param_inferencer.gleam"). -export([infer_insert_params/4, infer_insert_params_from_ir/3, extract_type_casts/3, extract_int_context_params/2, infer_in_params/5, infer_equality_params/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/query_analyzer/param_inferencer.gleam", 66). ?DOC(false). -spec map_insert_columns( sqlode@internal@model:engine(), sqlode@internal@model:catalog(), binary(), list(binary()), list(list(sqlode@internal@lexer:token())), integer(), gleam@dict:dict(binary(), integer()), list({integer(), sqlode@internal@model:column()}) ) -> list({integer(), sqlode@internal@model:column()}). map_insert_columns( Engine, Catalog, Table_name, Columns, Values, Occurrence, Seen, Acc ) -> case {Columns, Values} of {[], _} -> Acc; {_, []} -> Acc; {[Column_name | Rest_columns], [Value_tokens | Rest_values]} -> Value_placeholder = case Value_tokens of [{placeholder, P}] -> {some, P}; [{keyword, <<"cast"/utf8>>}, l_paren, {placeholder, P@1}, {keyword, <<"as"/utf8>>}, _, r_paren] -> {some, P@1}; _ -> none end, {Maybe_index, Next_occurrence, Updated_seen} = case Value_placeholder of {some, P@2} -> sqlode@internal@query_analyzer@placeholder:resolve_index( Engine, P@2, Occurrence, Seen ); none -> {none, Occurrence, Seen} end, Acc@1 = case Maybe_index of {some, Index} -> case sqlode@internal@query_analyzer@context:find_column( Catalog, Table_name, Column_name ) of {some, Column} -> [{Index, Column} | Acc]; none -> Acc end; none -> Acc end, map_insert_columns( Engine, Catalog, Table_name, Rest_columns, Rest_values, Next_occurrence, Updated_seen, Acc@1 ) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 18). ?DOC(false). -spec infer_insert_params( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@model:engine(), list(sqlode@internal@lexer:token()), sqlode@internal@model:catalog() ) -> list({integer(), sqlode@internal@model:column()}). infer_insert_params(_, Engine, Tokens, Catalog) -> case sqlode@internal@query_analyzer@token_utils:find_insert_parts(Tokens) of {some, Parts} -> _pipe = map_insert_columns( Engine, Catalog, erlang:element(2, Parts), erlang:element(3, Parts), erlang:element(4, Parts), 1, maps:new(), [] ), lists:reverse(_pipe); none -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 44). ?DOC(false). -spec infer_insert_params_from_ir( sqlode@internal@model:engine(), sqlode@internal@query_ir:sql_statement(), sqlode@internal@model:catalog() ) -> list({integer(), sqlode@internal@model:column()}). infer_insert_params_from_ir(Engine, Statement, Catalog) -> case Statement of {insert_statement, Table_name, Columns, Value_groups, _} -> _pipe = map_insert_columns( Engine, Catalog, Table_name, Columns, Value_groups, 1, maps:new(), [] ), lists:reverse(_pipe); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 417). ?DOC(false). -spec is_comparison_op(binary()) -> boolean(). is_comparison_op(Op) -> ((((((Op =:= <<"="/utf8>>) orelse (Op =:= <<"!="/utf8>>)) orelse (Op =:= <<"<>"/utf8>>)) orelse (Op =:= <<"<"/utf8>>)) orelse (Op =:= <<">"/utf8>>)) orelse (Op =:= <<"<="/utf8>>)) orelse (Op =:= <<">="/utf8>>). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 457). ?DOC(false). -spec column_of(sqlode@internal@query_ir:expr()) -> gleam@option:option({gleam@option:option(binary()), binary()}). column_of(Expr) -> case Expr of {column_ref, Table, Name} -> {some, {Table, Name}}; _ -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 464). ?DOC(false). -spec param_of(sqlode@internal@query_ir:expr()) -> gleam@option:option(binary()). param_of(Expr) -> case Expr of {param, _, Raw} -> {some, Raw}; {cast, Inner, _} -> param_of(Inner); _ -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 236). ?DOC(false). -spec assignment_match(sqlode@internal@query_ir:assignment()) -> {ok, sqlode@internal@query_analyzer@token_utils:equality_match()} | {error, nil}. assignment_match(Assignment) -> case param_of(erlang:element(3, Assignment)) of {some, Raw} -> {ok, {equality_match, sqlode@internal@naming:normalize_identifier( erlang:element(2, Assignment) ), none, Raw}}; none -> {error, nil} end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 484). ?DOC(false). -spec normalize_table_qualifier(gleam@option:option(binary())) -> gleam@option:option(binary()). normalize_table_qualifier(Table) -> case Table of {some, T} -> {some, string:lowercase(T)}; none -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 472). ?DOC(false). -spec build_match(gleam@option:option(binary()), binary(), binary()) -> sqlode@internal@query_analyzer@token_utils:equality_match(). build_match(Table, Name, Placeholder) -> {equality_match, sqlode@internal@naming:normalize_identifier(Name), normalize_table_qualifier(Table), Placeholder}. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 433). ?DOC(false). -spec comparison_match( sqlode@internal@query_ir:expr(), sqlode@internal@query_ir:expr() ) -> gleam@option:option(sqlode@internal@query_analyzer@token_utils:equality_match()). comparison_match(Left, Right) -> case {column_of(Left), param_of(Right)} of {{some, {Table, Name}}, {some, Raw}} -> {some, build_match(Table, Name, Raw)}; {_, _} -> case {column_of(Right), param_of(Left)} of {{some, {Table@1, Name@1}}, {some, Raw@1}} -> {some, build_match(Table@1, Name@1, Raw@1)}; {_, _} -> none end end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 447). ?DOC(false). -spec like_match( sqlode@internal@query_ir:expr(), sqlode@internal@query_ir:expr() ) -> gleam@option:option(sqlode@internal@query_analyzer@token_utils:equality_match()). like_match(Subject, Pattern) -> case {column_of(Subject), param_of(Pattern)} of {{some, {Table, Name}}, {some, Raw}} -> {some, build_match(Table, Name, Raw)}; {_, _} -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 705). ?DOC(false). -spec in_list_match( sqlode@internal@query_ir:expr(), list(sqlode@internal@query_ir:expr()) ) -> gleam@option:option(sqlode@internal@query_analyzer@token_utils:equality_match()). in_list_match(Subject, Values) -> case Values of [Only] -> case {column_of(Subject), param_of(Only)} of {{some, {Table, Name}}, {some, Raw}} -> {some, build_match(Table, Name, Raw)}; {_, _} -> none end; _ -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 737). ?DOC(false). -spec quantified_param_of(sqlode@internal@query_ir:expr()) -> gleam@option:option(binary()). quantified_param_of(Expr) -> case Expr of {tuple, [Only]} -> param_of(Only); _ -> param_of(Expr) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 726). ?DOC(false). -spec quantified_match( sqlode@internal@query_ir:expr(), sqlode@internal@query_ir:quantifier(), sqlode@internal@query_ir:expr() ) -> gleam@option:option(sqlode@internal@query_analyzer@token_utils:equality_match()). quantified_match(Left, _, Right) -> case {column_of(Left), quantified_param_of(Right)} of {{some, {Table, Name}}, {some, Raw}} -> {some, build_match(Table, Name, Raw)}; {_, _} -> none end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 755). ?DOC(false). -spec scan_token_matches( sqlode@internal@model:engine(), sqlode@internal@model:catalog(), binary(), list(binary()), list(sqlode@internal@query_analyzer@token_utils:equality_match()), integer(), gleam@dict:dict(binary(), integer()), list({integer(), sqlode@internal@model:column()}) ) -> {ok, list({integer(), sqlode@internal@model:column()})} | {error, sqlode@internal@query_analyzer@context:analysis_error()}. scan_token_matches( Engine, Catalog, Query_name, All_tables, Matches, Occurrence, Seen, Acc ) -> case Matches of [] -> {ok, Acc}; [Match | Rest] -> {Maybe_index, Next_occurrence, Updated_seen} = sqlode@internal@query_analyzer@placeholder:resolve_index( Engine, erlang:element(4, Match), Occurrence, Seen ), case Maybe_index of none -> scan_token_matches( Engine, Catalog, Query_name, All_tables, Rest, Next_occurrence, Updated_seen, Acc ); {some, Index} -> Lookup = case erlang:element(3, Match) of {some, Table} -> {ok, begin _pipe = sqlode@internal@query_analyzer@context:find_column( Catalog, Table, erlang:element(2, Match) ), gleam@option:map( _pipe, fun(Col) -> {Table, Col} end ) end}; none -> sqlode@internal@query_analyzer@context:find_column_in_tables( Catalog, All_tables, erlang:element(2, Match) ) end, case Lookup of {error, Matching_tables} -> {error, {ambiguous_column_name, Query_name, erlang:element(2, Match), Matching_tables}}; {ok, {some, {_, Column}}} -> scan_token_matches( Engine, Catalog, Query_name, All_tables, Rest, Next_occurrence, Updated_seen, [{Index, {column, erlang:element(2, Column), erlang:element(3, Column), false}} | Acc] ); {ok, none} -> scan_token_matches( Engine, Catalog, Query_name, All_tables, Rest, Next_occurrence, Updated_seen, Acc ) end end end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 989). ?DOC(false). -spec resolve_placeholder_index( sqlode@internal@model:engine(), binary(), integer() ) -> integer(). resolve_placeholder_index(Engine, Raw, Occurrence) -> case Engine of postgre_s_q_l -> case begin _pipe = Raw, _pipe@1 = gleam@string:replace(_pipe, <<"$"/utf8>>, <<""/utf8>>), gleam_stdlib:parse_int(_pipe@1) end of {ok, N} -> N; {error, _} -> Occurrence end; _ -> Occurrence end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 945). ?DOC(false). -spec scan_cast_hints( list(sqlode@internal@lexer:token()), sqlode@internal@model:engine(), integer(), list({integer(), binary()}) ) -> list({integer(), binary()}). scan_cast_hints(Tokens, Engine, Occurrence, Acc) -> case Tokens of [] -> lists:reverse(Acc); [{keyword, <<"cast"/utf8>>}, l_paren, {placeholder, Raw}, {keyword, <<"as"/utf8>>}, {ident, T}, r_paren | Rest] -> Index = resolve_placeholder_index(Engine, Raw, Occurrence), scan_cast_hints( Rest, Engine, Occurrence + 1, [{Index, string:lowercase(T)} | Acc] ); [{keyword, <<"cast"/utf8>>}, l_paren, {placeholder, Raw@1}, {keyword, <<"as"/utf8>>}, {keyword, T@1}, r_paren | Rest@1] -> Index@1 = resolve_placeholder_index(Engine, Raw@1, Occurrence), scan_cast_hints( Rest@1, Engine, Occurrence + 1, [{Index@1, T@1} | Acc] ); [{placeholder, _} | Rest@2] -> scan_cast_hints(Rest@2, Engine, Occurrence + 1, Acc); [_ | Rest@3] -> scan_cast_hints(Rest@3, Engine, Occurrence, Acc) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 1008). ?DOC(false). -spec cast_type_to_scalar(binary()) -> {ok, sqlode@internal@model:scalar_type()} | {error, nil}. cast_type_to_scalar(Type_name) -> sqlode@internal@model:parse_sql_type(gleam@string:trim(Type_name)). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 902). ?DOC(false). -spec extract_type_casts( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@model:engine(), list(sqlode@internal@lexer:token()) ) -> {ok, gleam@dict:dict(integer(), sqlode@internal@model:scalar_type())} | {error, {integer(), binary()}}. extract_type_casts(_, Engine, Tokens) -> Pg_casts = sqlode@internal@query_analyzer@token_utils:find_type_casts( Tokens ), gleam@result:'try'( gleam@list:try_fold( Pg_casts, maps:new(), fun(D, Cast) -> case begin _pipe = erlang:element(2, Cast), _pipe@1 = gleam@string:replace( _pipe, <<"$"/utf8>>, <<""/utf8>> ), gleam_stdlib:parse_int(_pipe@1) end of {ok, Index} -> case cast_type_to_scalar(erlang:element(3, Cast)) of {ok, Scalar_type} -> {ok, gleam@dict:insert(D, Index, Scalar_type)}; {error, nil} -> {error, {Index, gleam@string:trim( erlang:element(3, Cast) )}} end; {error, _} -> {ok, D} end end ), fun(Pg_dict) -> Cast_hints = scan_cast_hints(Tokens, Engine, 1, []), gleam@result:'try'( gleam@list:try_fold( Cast_hints, Pg_dict, fun(D@1, Pair) -> {Index@1, Type_name} = Pair, case cast_type_to_scalar(Type_name) of {ok, Scalar_type@1} -> {ok, gleam@dict:insert( D@1, Index@1, Scalar_type@1 )}; {error, nil} -> {error, {Index@1, gleam@string:trim(Type_name)}} end end ), fun(Cast_dict) -> {ok, Cast_dict} end ) end ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 1047). ?DOC(false). -spec scan_int_context_indices( list(sqlode@internal@lexer:token()), sqlode@internal@model:engine(), integer(), boolean(), list(integer()) ) -> list(integer()). scan_int_context_indices(Tokens, Engine, Occurrence, In_int_context, Acc) -> case Tokens of [] -> lists:reverse(Acc); [{keyword, K} | Rest] -> Lower = string:lowercase(K), Next_state = case Lower of <<"limit"/utf8>> -> true; <<"offset"/utf8>> -> true; <<"union"/utf8>> -> false; <<"except"/utf8>> -> false; <<"intersect"/utf8>> -> false; <<"returning"/utf8>> -> false; <<"for"/utf8>> -> false; _ -> In_int_context end, scan_int_context_indices(Rest, Engine, Occurrence, Next_state, Acc); [{placeholder, Raw} | Rest@1] -> Assigned = case Engine of postgre_s_q_l -> case begin _pipe = Raw, _pipe@1 = gleam@string:replace( _pipe, <<"$"/utf8>>, <<""/utf8>> ), gleam_stdlib:parse_int(_pipe@1) end of {ok, N} -> N; {error, _} -> Occurrence end; _ -> Occurrence end, Acc@1 = case In_int_context of true -> [Assigned | Acc]; false -> Acc end, scan_int_context_indices( Rest@1, Engine, Occurrence + 1, In_int_context, Acc@1 ); [_ | Rest@2] -> scan_int_context_indices( Rest@2, Engine, Occurrence, In_int_context, Acc ) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 1032). ?DOC(false). -spec extract_int_context_params( list(sqlode@internal@lexer:token()), sqlode@internal@model:engine() ) -> gleam@dict:dict(integer(), sqlode@internal@model:scalar_type()). extract_int_context_params(Tokens, Engine) -> Int_indices = scan_int_context_indices(Tokens, Engine, 1, false, []), gleam@list:fold( Int_indices, maps:new(), fun(D, Idx) -> gleam@dict:insert(D, Idx, int_type) end ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 560). ?DOC(false). -spec walk_set_op_iq(gleam@option:option(sqlode@internal@query_ir:set_op())) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_set_op_iq(Set_op) -> case Set_op of {some, {set_op, _, _, Right}} -> walk_select_core_iq(Right); none -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 534). ?DOC(false). -spec walk_select_core_iq(sqlode@internal@query_ir:select_core()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_select_core_iq(Core) -> lists:append( [gleam@list:flat_map(erlang:element(3, Core), fun walk_select_item_iq/1), gleam@list:flat_map( erlang:element(4, Core), fun walk_from_item_joins_iq/1 ), walk_optional_expr_iq(erlang:element(5, Core)), gleam@list:flat_map(erlang:element(6, Core), fun walk_expr_iq/1), walk_optional_expr_iq(erlang:element(7, Core)), gleam@list:flat_map( erlang:element(8, Core), fun walk_order_key_iq/1 ), walk_optional_expr_iq(erlang:element(9, Core)), walk_optional_expr_iq(erlang:element(10, Core)), walk_set_op_iq(erlang:element(11, Core))] ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 556). ?DOC(false). -spec walk_order_key_iq(sqlode@internal@query_ir:order_key()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_order_key_iq(Key) -> walk_expr_iq(erlang:element(2, Key)). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 623). ?DOC(false). -spec walk_expr_iq(sqlode@internal@query_ir:expr()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_expr_iq(Expr) -> case Expr of {in_expr, Subject, {in_list, Values}, _} -> case in_list_match(Subject, Values) of {some, M} -> [M]; none -> lists:append( walk_expr_iq(Subject), gleam@list:flat_map(Values, fun walk_expr_iq/1) ) end; {in_expr, Subject@1, Source, _} -> lists:append(walk_expr_iq(Subject@1), walk_in_source_iq(Source)); {quantified, _, Left, Quantifier, Right} -> case quantified_match(Left, Quantifier, Right) of {some, M@1} -> [M@1]; none -> lists:append(walk_expr_iq(Left), walk_expr_iq(Right)) end; {binary, _, Left@1, Right@1} -> lists:append(walk_expr_iq(Left@1), walk_expr_iq(Right@1)); {like_expr, Subject@2, _, Pattern, _, _} -> lists:append(walk_expr_iq(Subject@2), walk_expr_iq(Pattern)); {unary, _, Arg} -> walk_expr_iq(Arg); {between, Subject@3, Low, High, _} -> lists:append( [walk_expr_iq(Subject@3), walk_expr_iq(Low), walk_expr_iq(High)] ); {is_check, Subject@4, _, _} -> walk_expr_iq(Subject@4); {'case', Scrutinee, Branches, Else_} -> lists:append( [walk_optional_expr_iq(Scrutinee), gleam@list:flat_map( Branches, fun(B) -> lists:append( walk_expr_iq(erlang:element(2, B)), walk_expr_iq(erlang:element(3, B)) ) end ), walk_optional_expr_iq(Else_)] ); {cast, Subject@5, _} -> walk_expr_iq(Subject@5); {func, _, Args, _, Filter, Over} -> lists:append( [gleam@list:flat_map( Args, fun(A) -> walk_expr_iq(erlang:element(2, A)) end ), walk_optional_expr_iq(Filter), case Over of {some, Spec} -> walk_window_spec_iq(Spec); none -> [] end] ); {tuple, Elements} -> gleam@list:flat_map(Elements, fun walk_expr_iq/1); {array_lit, Elements@1} -> gleam@list:flat_map(Elements@1, fun walk_expr_iq/1); {exists, Core, _} -> walk_select_core_iq(Core); {scalar_subquery, Core@1} -> walk_select_core_iq(Core@1); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 569). ?DOC(false). -spec walk_window_spec_iq(sqlode@internal@query_ir:window_spec()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_window_spec_iq(Spec) -> lists:append( [gleam@list:flat_map(erlang:element(2, Spec), fun walk_expr_iq/1), gleam@list:flat_map( erlang:element(3, Spec), fun walk_order_key_iq/1 )] ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 578). ?DOC(false). -spec walk_select_item_iq(sqlode@internal@query_ir:select_item_ex()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_select_item_iq(Item) -> case Item of {expr_item, Expr, _} -> walk_expr_iq(Expr); {star_ex, _} -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 601). ?DOC(false). -spec walk_join_on_iq(sqlode@internal@query_ir:join_on()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_join_on_iq(On) -> case On of {join_on_expr, Expr} -> walk_expr_iq(Expr); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 587). ?DOC(false). -spec walk_from_item_joins_iq(sqlode@internal@query_ir:from_item_ex()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_from_item_joins_iq(From) -> case From of {from_join, Left, Right, _, On, _} -> lists:append( [walk_from_item_joins_iq(Left), walk_from_item_joins_iq(Right), walk_join_on_iq(On)] ); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 608). ?DOC(false). -spec walk_optional_expr_iq( gleam@option:option(sqlode@internal@query_ir:expr()) ) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_optional_expr_iq(Expr) -> case Expr of {some, E} -> walk_expr_iq(E); none -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 513). ?DOC(false). -spec find_in_quantified_matches_in_stmt(sqlode@internal@query_ir:stmt()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). find_in_quantified_matches_in_stmt(Stmt) -> case Stmt of {select_stmt, _, Core} -> walk_select_core_iq(Core); {update_stmt, _, _, _, Assignments, _, Where_, _} -> lists:append( [gleam@list:flat_map( Assignments, fun(A) -> walk_expr_iq(erlang:element(3, A)) end ), walk_optional_expr_iq(Where_)] ); {delete_stmt, _, _, _, _, Where_@1, _} -> walk_optional_expr_iq(Where_@1); {insert_stmt, _, _, _, _, _, _} -> []; {unstructured_stmt, _, _} -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 688). ?DOC(false). -spec walk_in_source_iq(sqlode@internal@query_ir:in_source()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_in_source_iq(Source) -> case Source of {in_subquery, Core} -> walk_select_core_iq(Core); {in_list, Values} -> gleam@list:flat_map(Values, fun walk_expr_iq/1); {in_slice_macro, _} -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 853). ?DOC(false). -spec infer_in_params( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@model:engine(), binary(), list(sqlode@internal@lexer:token()), sqlode@internal@model:catalog() ) -> {ok, list({integer(), sqlode@internal@model:column()})} | {error, sqlode@internal@query_analyzer@context:analysis_error()}. infer_in_params(_, Engine, Query_name, Tokens, Catalog) -> Main_tokens = sqlode@internal@query_analyzer@token_utils:strip_leading_with( Tokens ), All_tables = sqlode@internal@query_analyzer@token_utils:extract_table_names( Main_tokens ), case All_tables of [] -> {ok, []}; _ -> Matches = case sqlode@internal@query_analyzer@expr_parser:parse_stmt( Tokens, Engine ) of {unstructured_stmt, _, _} -> lists:append( sqlode@internal@query_analyzer@token_utils:find_in_patterns( Main_tokens ), sqlode@internal@query_analyzer@token_utils:find_quantified_patterns( Main_tokens ) ); {insert_stmt, _, _, _, _, _, _} -> lists:append( sqlode@internal@query_analyzer@token_utils:find_in_patterns( Main_tokens ), sqlode@internal@query_analyzer@token_utils:find_quantified_patterns( Main_tokens ) ); Stmt -> find_in_quantified_matches_in_stmt(Stmt) end, _pipe = scan_token_matches( Engine, Catalog, Query_name, All_tables, Matches, 1, maps:new(), [] ), gleam@result:map(_pipe, fun lists:reverse/1) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 279). ?DOC(false). -spec walk_set_op(gleam@option:option(sqlode@internal@query_ir:set_op())) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_set_op(Set_op) -> case Set_op of {some, {set_op, _, _, Right}} -> walk_select_core(Right); none -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 250). ?DOC(false). -spec walk_select_core(sqlode@internal@query_ir:select_core()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_select_core(Core) -> lists:append( [gleam@list:flat_map(erlang:element(3, Core), fun walk_select_item/1), gleam@list:flat_map( erlang:element(4, Core), fun walk_from_item_joins/1 ), walk_optional_expr(erlang:element(5, Core)), gleam@list:flat_map(erlang:element(6, Core), fun walk_expr/1), walk_optional_expr(erlang:element(7, Core)), gleam@list:flat_map(erlang:element(8, Core), fun walk_order_key/1), walk_optional_expr(erlang:element(9, Core)), walk_optional_expr(erlang:element(10, Core)), walk_set_op(erlang:element(11, Core))] ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 275). ?DOC(false). -spec walk_order_key(sqlode@internal@query_ir:order_key()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_order_key(Key) -> walk_expr(erlang:element(2, Key)). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 347). ?DOC(false). -spec walk_expr(sqlode@internal@query_ir:expr()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_expr(Expr) -> case Expr of {binary, Op, Left, Right} -> case is_comparison_op(Op) of true -> case comparison_match(Left, Right) of {some, M} -> [M]; none -> lists:append(walk_expr(Left), walk_expr(Right)) end; false -> lists:append(walk_expr(Left), walk_expr(Right)) end; {like_expr, Subject, _, Pattern, _, _} -> case like_match(Subject, Pattern) of {some, M@1} -> [M@1]; none -> lists:append(walk_expr(Subject), walk_expr(Pattern)) end; {unary, _, Arg} -> walk_expr(Arg); {between, Subject@1, Low, High, _} -> lists:append( [walk_expr(Subject@1), walk_expr(Low), walk_expr(High)] ); {is_check, Subject@2, _, _} -> walk_expr(Subject@2); {'case', Scrutinee, Branches, Else_} -> lists:append( [walk_optional_expr(Scrutinee), gleam@list:flat_map( Branches, fun(B) -> lists:append( walk_expr(erlang:element(2, B)), walk_expr(erlang:element(3, B)) ) end ), walk_optional_expr(Else_)] ); {cast, Subject@3, _} -> walk_expr(Subject@3); {func, _, Args, _, Filter, Over} -> lists:append( [gleam@list:flat_map( Args, fun(A) -> walk_expr(erlang:element(2, A)) end ), walk_optional_expr(Filter), case Over of {some, Spec} -> walk_window_spec(Spec); none -> [] end] ); {tuple, Elements} -> gleam@list:flat_map(Elements, fun walk_expr/1); {array_lit, Elements@1} -> gleam@list:flat_map(Elements@1, fun walk_expr/1); {exists, Core, _} -> walk_select_core(Core); {scalar_subquery, Core@1} -> walk_select_core(Core@1); {in_expr, Subject@4, Source, _} -> lists:append(walk_expr(Subject@4), walk_in_source(Source)); {quantified, _, Left@1, _, Right@1} -> lists:append(walk_expr(Left@1), walk_expr(Right@1)); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 288). ?DOC(false). -spec walk_window_spec(sqlode@internal@query_ir:window_spec()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_window_spec(Spec) -> lists:append( [gleam@list:flat_map(erlang:element(2, Spec), fun walk_expr/1), gleam@list:flat_map(erlang:element(3, Spec), fun walk_order_key/1)] ). -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 297). ?DOC(false). -spec walk_select_item(sqlode@internal@query_ir:select_item_ex()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_select_item(Item) -> case Item of {expr_item, Expr, _} -> walk_expr(Expr); {star_ex, _} -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 323). ?DOC(false). -spec walk_join_on(sqlode@internal@query_ir:join_on()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_join_on(On) -> case On of {join_on_expr, Expr} -> walk_expr(Expr); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 306). ?DOC(false). -spec walk_from_item_joins(sqlode@internal@query_ir:from_item_ex()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_from_item_joins(From) -> case From of {from_join, Left, Right, _, On, _} -> lists:append( [walk_from_item_joins(Left), walk_from_item_joins(Right), walk_join_on(On)] ); _ -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 330). ?DOC(false). -spec walk_optional_expr(gleam@option:option(sqlode@internal@query_ir:expr())) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_optional_expr(Expr) -> case Expr of {some, E} -> walk_expr(E); none -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 206). ?DOC(false). -spec find_equality_matches_in_stmt(sqlode@internal@query_ir:stmt()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). find_equality_matches_in_stmt(Stmt) -> case Stmt of {select_stmt, _, Core} -> walk_select_core(Core); {update_stmt, _, _, _, Assignments, _, Where_, _} -> lists:append( [gleam@list:filter_map(Assignments, fun assignment_match/1), gleam@list:flat_map( Assignments, fun(A) -> walk_expr(erlang:element(3, A)) end ), walk_optional_expr(Where_)] ); {delete_stmt, _, _, _, _, Where_@1, _} -> walk_optional_expr(Where_@1); {insert_stmt, _, _, _, _, _, _} -> []; {unstructured_stmt, _, _} -> [] end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 132). ?DOC(false). -spec infer_equality_params( sqlode@internal@query_analyzer@context:analyzer_context(), sqlode@internal@model:engine(), binary(), list(sqlode@internal@lexer:token()), sqlode@internal@model:catalog() ) -> {ok, list({integer(), sqlode@internal@model:column()})} | {error, sqlode@internal@query_analyzer@context:analysis_error()}. infer_equality_params(_, Engine, Query_name, Tokens, Catalog) -> Main_tokens = sqlode@internal@query_analyzer@token_utils:strip_leading_with( Tokens ), All_tables = sqlode@internal@query_analyzer@token_utils:extract_table_names( Main_tokens ), case All_tables of [] -> {ok, []}; _ -> Matches = case sqlode@internal@query_analyzer@expr_parser:parse_stmt( Tokens, Engine ) of {unstructured_stmt, _, _} -> sqlode@internal@query_analyzer@token_utils:find_equality_patterns( Main_tokens ); {insert_stmt, _, _, _, _, _, _} -> sqlode@internal@query_analyzer@token_utils:find_equality_patterns( Main_tokens ); Stmt -> find_equality_matches_in_stmt(Stmt) end, _pipe = scan_token_matches( Engine, Catalog, Query_name, All_tables, Matches, 1, maps:new(), [] ), gleam@result:map(_pipe, fun lists:reverse/1) end. -file("src/sqlode/internal/query_analyzer/param_inferencer.gleam", 409). ?DOC(false). -spec walk_in_source(sqlode@internal@query_ir:in_source()) -> list(sqlode@internal@query_analyzer@token_utils:equality_match()). walk_in_source(Source) -> case Source of {in_subquery, Core} -> walk_select_core(Core); {in_list, Values} -> gleam@list:flat_map(Values, fun walk_expr/1); {in_slice_macro, _} -> [] end.