Current section

Files

Jump to
sqlode src sqlode@query_analyzer@param_inferencer.erl
Raw

src/sqlode@query_analyzer@param_inferencer.erl

-module(sqlode@query_analyzer@param_inferencer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/query_analyzer/param_inferencer.gleam").
-export([infer_insert_params/4, infer_insert_params_from_ir/3, infer_equality_params/4, infer_in_params/4, extract_type_casts/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/sqlode/query_analyzer/param_inferencer.gleam", 61).
-spec map_insert_columns(
sqlode@model:engine(),
sqlode@model:catalog(),
binary(),
list(binary()),
list(list(sqlode@lexer:token())),
integer(),
gleam@dict:dict(binary(), integer()),
list({integer(), sqlode@model:column()})
) -> list({integer(), sqlode@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};
_ ->
none
end,
{Maybe_index, Next_occurrence, Updated_seen} = case Value_placeholder of
{some, P@1} ->
sqlode@query_analyzer@placeholder:resolve_index(
Engine,
P@1,
Occurrence,
Seen
);
none ->
{none, Occurrence, Seen}
end,
Acc@1 = case Maybe_index of
{some, Index} ->
case sqlode@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/query_analyzer/param_inferencer.gleam", 13).
-spec infer_insert_params(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
list(sqlode@lexer:token()),
sqlode@model:catalog()
) -> list({integer(), sqlode@model:column()}).
infer_insert_params(_, Engine, Tokens, Catalog) ->
case sqlode@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/query_analyzer/param_inferencer.gleam", 39).
?DOC(
" Structured IR variant of `infer_insert_params`. Consumes the\n"
" pre-parsed `InsertStatement` directly instead of re-scanning\n"
" the token list.\n"
).
-spec infer_insert_params_from_ir(
sqlode@model:engine(),
sqlode@query_ir:sql_statement(),
sqlode@model:catalog()
) -> list({integer(), sqlode@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/query_analyzer/param_inferencer.gleam", 137).
-spec scan_token_matches(
sqlode@model:engine(),
sqlode@model:catalog(),
binary(),
list(binary()),
list(sqlode@query_analyzer@token_utils:equality_match()),
integer(),
gleam@dict:dict(binary(), integer()),
list({integer(), sqlode@model:column()})
) -> list({integer(), sqlode@model:column()}).
scan_token_matches(
Engine,
Catalog,
Primary_table,
All_tables,
Matches,
Occurrence,
Seen,
Acc
) ->
case Matches of
[] ->
Acc;
[Match | Rest] ->
{Maybe_index, Next_occurrence, Updated_seen} = sqlode@query_analyzer@placeholder:resolve_index(
Engine,
erlang:element(4, Match),
Occurrence,
Seen
),
Acc@1 = case Maybe_index of
{some, Index} ->
Found = case erlang:element(3, Match) of
{some, Table} ->
sqlode@query_analyzer@context:find_column(
Catalog,
Table,
erlang:element(2, Match)
);
none ->
case sqlode@query_analyzer@context:find_column_in_tables(
Catalog,
All_tables,
erlang:element(2, Match)
) of
{ok, {some, Pair}} ->
{some, erlang:element(2, Pair)};
_ ->
none
end
end,
case Found of
{some, Column} ->
[{Index, Column} | Acc];
none ->
case sqlode@query_analyzer@context:find_column(
Catalog,
Primary_table,
erlang:element(2, Match)
) of
{some, Column@1} ->
[{Index, Column@1} | Acc];
none ->
Acc
end
end;
none ->
Acc
end,
scan_token_matches(
Engine,
Catalog,
Primary_table,
All_tables,
Rest,
Next_occurrence,
Updated_seen,
Acc@1
)
end.
-file("src/sqlode/query_analyzer/param_inferencer.gleam", 110).
-spec infer_equality_params(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
list(sqlode@lexer:token()),
sqlode@model:catalog()
) -> list({integer(), sqlode@model:column()}).
infer_equality_params(_, Engine, Tokens, Catalog) ->
All_tables = sqlode@query_analyzer@token_utils:extract_table_names(Tokens),
case All_tables of
[] ->
[];
[Primary | _] ->
Matches = sqlode@query_analyzer@token_utils:find_equality_patterns(
Tokens
),
_pipe = scan_token_matches(
Engine,
Catalog,
Primary,
All_tables,
Matches,
1,
maps:new(),
[]
),
lists:reverse(_pipe)
end.
-file("src/sqlode/query_analyzer/param_inferencer.gleam", 199).
-spec infer_in_params(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
list(sqlode@lexer:token()),
sqlode@model:catalog()
) -> list({integer(), sqlode@model:column()}).
infer_in_params(_, Engine, Tokens, Catalog) ->
All_tables = sqlode@query_analyzer@token_utils:extract_table_names(Tokens),
case All_tables of
[] ->
[];
[Primary | _] ->
Matches = sqlode@query_analyzer@token_utils:find_in_patterns(Tokens),
_pipe = scan_token_matches(
Engine,
Catalog,
Primary,
All_tables,
Matches,
1,
maps:new(),
[]
),
lists:reverse(_pipe)
end.
-file("src/sqlode/query_analyzer/param_inferencer.gleam", 253).
-spec cast_type_to_scalar(binary()) -> {ok, sqlode@model:scalar_type()} |
{error, nil}.
cast_type_to_scalar(Type_name) ->
sqlode@model:parse_sql_type(gleam@string:trim(Type_name)).
-file("src/sqlode/query_analyzer/param_inferencer.gleam", 226).
-spec extract_type_casts(
sqlode@query_analyzer@context:analyzer_context(),
sqlode@model:engine(),
list(sqlode@lexer:token())
) -> {ok, gleam@dict:dict(integer(), sqlode@model:scalar_type())} |
{error, {integer(), binary()}}.
extract_type_casts(_, Engine, Tokens) ->
case Engine of
postgre_s_q_l ->
Casts = sqlode@query_analyzer@token_utils:find_type_casts(Tokens),
gleam@list:try_fold(
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
);
_ ->
{ok, maps:new()}
end.