Current section
Files
Jump to
Current section
Files
src/sqlode@internal@query_analyzer@token_utils.erl
-module(sqlode@internal@query_analyzer@token_utils).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/internal/query_analyzer/token_utils.gleam").
-export([skip_parens/2, read_table_name/1, strip_leading_with/1, extract_placeholders/1, collect_paren_contents/1, read_subquery_alias/1, extract_table_names/1, split_on_commas/1, strip_insert_or_action/1, find_equality_patterns/1, find_quantified_patterns/1, find_in_patterns/1, find_type_casts/1, structure_tokens/1]).
-export_type([insert_parts/0, equality_match/0, type_cast/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 insert_parts() :: {insert_parts,
binary(),
list(binary()),
list(list(sqlode@internal@lexer:token()))}.
-type equality_match() :: {equality_match,
binary(),
gleam@option:option(binary()),
binary()}.
-type type_cast() :: {type_cast, binary(), binary()}.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 154).
?DOC(false).
-spec skip_parens(list(sqlode@internal@lexer:token()), integer()) -> list(sqlode@internal@lexer:token()).
skip_parens(Tokens, Depth) ->
case Depth =< 0 of
true ->
Tokens;
false ->
case Tokens of
[] ->
[];
[l_paren | Rest] ->
skip_parens(Rest, Depth + 1);
[r_paren | Rest@1] ->
skip_parens(Rest@1, Depth - 1);
[_ | Rest@2] ->
skip_parens(Rest@2, Depth)
end
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 102).
?DOC(false).
-spec read_table_name(list(sqlode@internal@lexer:token())) -> {gleam@option:option(binary()),
list(sqlode@internal@lexer:token())}.
read_table_name(Tokens) ->
case Tokens of
[{ident, _}, dot, {ident, Name} | Rest] ->
{{some, string:lowercase(Name)}, Rest};
[{ident, Name@1} | Rest@1] ->
{{some, string:lowercase(Name@1)}, Rest@1};
[{quoted_ident, Name@2} | Rest@2] ->
{{some, string:lowercase(Name@2)}, Rest@2};
[l_paren | Rest@3] ->
Remaining = skip_parens(Rest@3, 1),
{none, Remaining};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 139).
?DOC(false).
-spec skip_with_body(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
skip_with_body(Tokens) ->
case Tokens of
[] ->
[];
[{keyword, Kw} | _] = T when (((Kw =:= <<"select"/utf8>>) orelse (Kw =:= <<"insert"/utf8>>)) orelse (Kw =:= <<"update"/utf8>>)) orelse (Kw =:= <<"delete"/utf8>>) ->
T;
[l_paren | Rest] ->
Remaining = skip_parens(Rest, 1),
skip_with_body(Remaining);
[_ | Rest@1] ->
skip_with_body(Rest@1)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 130).
?DOC(false).
-spec strip_leading_with(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
strip_leading_with(Tokens) ->
case Tokens of
[{keyword, <<"with"/utf8>>}, {keyword, <<"recursive"/utf8>>} | Rest] ->
skip_with_body(Rest);
[{keyword, <<"with"/utf8>>} | Rest@1] ->
skip_with_body(Rest@1);
_ ->
Tokens
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 172).
?DOC(false).
-spec extract_placeholders(list(sqlode@internal@lexer:token())) -> list(binary()).
extract_placeholders(Tokens) ->
gleam@list:filter_map(Tokens, fun(Token) -> case Token of
{placeholder, P} ->
{ok, P};
_ ->
{error, nil}
end end).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 190).
?DOC(false).
-spec collect_paren_loop(
list(sqlode@internal@lexer:token()),
integer(),
list(sqlode@internal@lexer:token())
) -> {list(sqlode@internal@lexer:token()), list(sqlode@internal@lexer:token())}.
collect_paren_loop(Tokens, Depth, Acc) ->
case Depth =< 0 of
true ->
{lists:reverse(Acc), Tokens};
false ->
case Tokens of
[] ->
{lists:reverse(Acc), []};
[l_paren | Rest] ->
collect_paren_loop(Rest, Depth + 1, [l_paren | Acc]);
[r_paren | Rest@1] ->
case Depth =:= 1 of
true ->
{lists:reverse(Acc), Rest@1};
false ->
collect_paren_loop(
Rest@1,
Depth - 1,
[r_paren | Acc]
)
end;
[Token | Rest@2] ->
collect_paren_loop(Rest@2, Depth, [Token | Acc])
end
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 184).
?DOC(false).
-spec collect_paren_contents(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
collect_paren_contents(Tokens) ->
collect_paren_loop(Tokens, 1, []).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 78).
?DOC(false).
-spec read_subquery_alias(list(sqlode@internal@lexer:token())) -> {gleam@option:option(binary()),
list(sqlode@internal@lexer:token())}.
read_subquery_alias(Tokens) ->
After_as = case Tokens of
[{keyword, <<"as"/utf8>>} | Rest] ->
Rest;
_ ->
Tokens
end,
case After_as of
[{ident, Name}, l_paren | Rest_after_lp] ->
{_, After_cols} = collect_paren_contents(Rest_after_lp),
{{some, string:lowercase(Name)}, After_cols};
[{quoted_ident, Name@1}, l_paren | Rest_after_lp@1] ->
{_, After_cols@1} = collect_paren_contents(Rest_after_lp@1),
{{some, string:lowercase(Name@1)}, After_cols@1};
[{ident, Name@2} | Rest@1] ->
{{some, string:lowercase(Name@2)}, Rest@1};
[{quoted_ident, Name@3} | Rest@2] ->
{{some, string:lowercase(Name@3)}, Rest@2};
_ ->
{none, After_as}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 14).
?DOC(false).
-spec table_names_loop(list(sqlode@internal@lexer:token()), list(binary())) -> list(binary()).
table_names_loop(Tokens, Acc) ->
case Tokens of
[] ->
lists:reverse(Acc);
[{keyword, <<"from"/utf8>>}, l_paren | Rest] ->
Remaining = skip_parens(Rest, 1),
case read_subquery_alias(Remaining) of
{{some, N}, After_alias} ->
table_names_loop(After_alias, [N | Acc]);
{none, _} ->
table_names_loop(Remaining, Acc)
end;
[{keyword, Kw} | Rest@1] when ((Kw =:= <<"from"/utf8>>) orelse (Kw =:= <<"into"/utf8>>)) orelse (Kw =:= <<"update"/utf8>>) ->
{Name, Remaining@1} = read_table_name(Rest@1),
case Name of
{some, N@1} ->
table_names_loop(Remaining@1, [N@1 | Acc]);
none ->
table_names_loop(Rest@1, Acc)
end;
[{keyword, <<"join"/utf8>>},
{keyword, <<"lateral"/utf8>>},
l_paren |
Rest@2] ->
Remaining@2 = skip_parens(Rest@2, 1),
case read_subquery_alias(Remaining@2) of
{{some, N@2}, After_alias@1} ->
table_names_loop(After_alias@1, [N@2 | Acc]);
{none, _} ->
table_names_loop(Remaining@2, Acc)
end;
[{keyword, <<"join"/utf8>>}, l_paren | Rest@3] ->
Remaining@3 = skip_parens(Rest@3, 1),
case read_subquery_alias(Remaining@3) of
{{some, N@3}, After_alias@2} ->
table_names_loop(After_alias@2, [N@3 | Acc]);
{none, _} ->
table_names_loop(Remaining@3, Acc)
end;
[{keyword, <<"lateral"/utf8>>}, l_paren | Rest@4] ->
Remaining@4 = skip_parens(Rest@4, 1),
case read_subquery_alias(Remaining@4) of
{{some, N@4}, After_alias@3} ->
table_names_loop(After_alias@3, [N@4 | Acc]);
{none, _} ->
table_names_loop(Remaining@4, Acc)
end;
[{keyword, <<"join"/utf8>>} | Rest@5] ->
{Name@1, Remaining@5} = read_table_name(Rest@5),
case Name@1 of
{some, N@5} ->
table_names_loop(Remaining@5, [N@5 | Acc]);
none ->
table_names_loop(Rest@5, Acc)
end;
[_ | Rest@6] ->
table_names_loop(Rest@6, Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 9).
?DOC(false).
-spec extract_table_names(list(sqlode@internal@lexer:token())) -> list(binary()).
extract_table_names(Tokens) ->
_pipe = table_names_loop(Tokens, []),
gleam@list:unique(_pipe).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 217).
?DOC(false).
-spec split_commas_loop(
list(sqlode@internal@lexer:token()),
integer(),
list(sqlode@internal@lexer:token()),
list(list(sqlode@internal@lexer:token()))
) -> list(list(sqlode@internal@lexer:token())).
split_commas_loop(Tokens, Depth, Current, Acc) ->
case Tokens of
[] ->
case Current of
[] ->
lists:reverse(Acc);
_ ->
lists:reverse([lists:reverse(Current) | Acc])
end;
[comma | Rest] when Depth =:= 0 ->
case Current of
[] ->
split_commas_loop(Rest, 0, [], Acc);
_ ->
split_commas_loop(
Rest,
0,
[],
[lists:reverse(Current) | Acc]
)
end;
[l_paren | Rest@1] ->
split_commas_loop(Rest@1, Depth + 1, [l_paren | Current], Acc);
[r_paren | Rest@2] ->
split_commas_loop(Rest@2, Depth - 1, [r_paren | Current], Acc);
[Token | Rest@3] ->
split_commas_loop(Rest@3, Depth, [Token | Current], Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 213).
?DOC(false).
-spec split_on_commas(list(sqlode@internal@lexer:token())) -> list(list(sqlode@internal@lexer:token())).
split_on_commas(Tokens) ->
split_commas_loop(Tokens, 0, [], []).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 290).
?DOC(false).
-spec is_insert_or_action_token(sqlode@internal@lexer:token()) -> boolean().
is_insert_or_action_token(Token) ->
case Token of
{keyword, <<"replace"/utf8>>} ->
true;
{keyword, <<"rollback"/utf8>>} ->
true;
{keyword, <<"ignore"/utf8>>} ->
true;
{keyword, <<"abort"/utf8>>} ->
true;
{keyword, <<"fail"/utf8>>} ->
true;
{ident, T} ->
((((((((T =:= <<"ignore"/utf8>>) orelse (T =:= <<"IGNORE"/utf8>>))
orelse (T =:= <<"Ignore"/utf8>>))
orelse (T =:= <<"abort"/utf8>>))
orelse (T =:= <<"ABORT"/utf8>>))
orelse (T =:= <<"Abort"/utf8>>))
orelse (T =:= <<"fail"/utf8>>))
orelse (T =:= <<"FAIL"/utf8>>))
orelse (T =:= <<"Fail"/utf8>>);
_ ->
false
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 279).
?DOC(false).
-spec strip_insert_or_action(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
strip_insert_or_action(Tokens) ->
case Tokens of
[{keyword, <<"or"/utf8>>}, Action | Rest] ->
case is_insert_or_action_token(Action) of
true ->
Rest;
false ->
Tokens
end;
_ ->
Tokens
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 356).
?DOC(false).
-spec skip_to_values(list(sqlode@internal@lexer:token())) -> gleam@option:option(list(sqlode@internal@lexer:token())).
skip_to_values(Tokens) ->
case Tokens of
[] ->
none;
[{keyword, <<"values"/utf8>>} | Rest] ->
{some, Rest};
[_ | Rest@1] ->
skip_to_values(Rest@1)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 316).
?DOC(false).
-spec parse_insert_after_into(list(sqlode@internal@lexer:token())) -> gleam@option:option(insert_parts()).
parse_insert_after_into(Tokens) ->
{Table_name_opt, Rest} = read_table_name(Tokens),
case Table_name_opt of
none ->
none;
{some, Table_name} ->
case Rest of
[l_paren | After_lparen] ->
{Col_tokens, After_cols} = collect_paren_contents(
After_lparen
),
Columns = begin
_pipe = split_on_commas(Col_tokens),
gleam@list:filter_map(_pipe, fun(Group) -> case Group of
[{ident, Name}] ->
{ok,
sqlode@internal@naming:normalize_identifier(
Name
)};
[{quoted_ident, Name@1}] ->
{ok,
sqlode@internal@naming:normalize_identifier(
Name@1
)};
_ ->
{error, nil}
end end)
end,
case skip_to_values(After_cols) of
none ->
none;
{some, After_values_kw} ->
case After_values_kw of
[l_paren | After_vlparen] ->
{Val_tokens, _} = collect_paren_contents(
After_vlparen
),
Values = split_on_commas(Val_tokens),
{some,
{insert_parts,
Table_name,
Columns,
Values}};
_ ->
none
end
end;
_ ->
none
end
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 259).
?DOC(false).
-spec find_insert_loop(list(sqlode@internal@lexer:token())) -> gleam@option:option(insert_parts()).
find_insert_loop(Tokens) ->
case Tokens of
[] ->
none;
[{keyword, <<"insert"/utf8>>} | Rest] ->
case strip_insert_or_action(Rest) of
[{keyword, <<"into"/utf8>>} | After_into] ->
parse_insert_after_into(After_into);
_ ->
find_insert_loop(Rest)
end;
[_ | Rest@1] ->
find_insert_loop(Rest@1)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 255).
?DOC(false).
-spec find_insert_parts(list(sqlode@internal@lexer:token())) -> gleam@option:option(insert_parts()).
find_insert_parts(Tokens) ->
find_insert_loop(Tokens).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 382).
?DOC(false).
-spec find_equality_loop(
list(sqlode@internal@lexer:token()),
list(equality_match())
) -> list(equality_match()).
find_equality_loop(Tokens, Acc) ->
case Tokens of
[] ->
Acc;
[{ident, T}, dot, {ident, C}, {operator, Op}, {placeholder, P} | Rest] when ((((((Op =:= <<"="/utf8>>) orelse (Op =:= <<"!="/utf8>>)) orelse (Op =:= <<"<>"/utf8>>)) orelse (Op =:= <<"<"/utf8>>)) orelse (Op =:= <<">"/utf8>>)) orelse (Op =:= <<"<="/utf8>>)) orelse (Op =:= <<">="/utf8>>) ->
find_equality_loop(
Rest,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C),
{some, string:lowercase(T)},
P} |
Acc]
);
[{ident, T@1},
dot,
{quoted_ident, C@1},
{operator, Op@1},
{placeholder, P@1} |
Rest@1] when ((((((Op@1 =:= <<"="/utf8>>) orelse (Op@1 =:= <<"!="/utf8>>)) orelse (Op@1 =:= <<"<>"/utf8>>)) orelse (Op@1 =:= <<"<"/utf8>>)) orelse (Op@1 =:= <<">"/utf8>>)) orelse (Op@1 =:= <<"<="/utf8>>)) orelse (Op@1 =:= <<">="/utf8>>) ->
find_equality_loop(
Rest@1,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@1),
{some, string:lowercase(T@1)},
P@1} |
Acc]
);
[{ident, C@2}, {operator, Op@2}, {placeholder, P@2} | Rest@2] when ((((((Op@2 =:= <<"="/utf8>>) orelse (Op@2 =:= <<"!="/utf8>>)) orelse (Op@2 =:= <<"<>"/utf8>>)) orelse (Op@2 =:= <<"<"/utf8>>)) orelse (Op@2 =:= <<">"/utf8>>)) orelse (Op@2 =:= <<"<="/utf8>>)) orelse (Op@2 =:= <<">="/utf8>>) ->
find_equality_loop(
Rest@2,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@2),
none,
P@2} |
Acc]
);
[{quoted_ident, C@3}, {operator, Op@3}, {placeholder, P@3} | Rest@3] when ((((((Op@3 =:= <<"="/utf8>>) orelse (Op@3 =:= <<"!="/utf8>>)) orelse (Op@3 =:= <<"<>"/utf8>>)) orelse (Op@3 =:= <<"<"/utf8>>)) orelse (Op@3 =:= <<">"/utf8>>)) orelse (Op@3 =:= <<"<="/utf8>>)) orelse (Op@3 =:= <<">="/utf8>>) ->
find_equality_loop(
Rest@3,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@3),
none,
P@3} |
Acc]
);
[{ident, T@2},
dot,
{ident, C@4},
{keyword, Kw},
{placeholder, P@4} |
Rest@4] when (Kw =:= <<"like"/utf8>>) orelse (Kw =:= <<"ilike"/utf8>>) ->
find_equality_loop(
Rest@4,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@4),
{some, string:lowercase(T@2)},
P@4} |
Acc]
);
[{ident, C@5}, {keyword, Kw@1}, {placeholder, P@5} | Rest@5] when (Kw@1 =:= <<"like"/utf8>>) orelse (Kw@1 =:= <<"ilike"/utf8>>) ->
find_equality_loop(
Rest@5,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@5),
none,
P@5} |
Acc]
);
[_ | Rest@6] ->
find_equality_loop(Rest@6, Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 377).
?DOC(false).
-spec find_equality_patterns(list(sqlode@internal@lexer:token())) -> list(equality_match()).
find_equality_patterns(Tokens) ->
_pipe = find_equality_loop(Tokens, []),
lists:reverse(_pipe).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 535).
?DOC(false).
-spec find_quantified_loop(
list(sqlode@internal@lexer:token()),
list(equality_match())
) -> list(equality_match()).
find_quantified_loop(Tokens, Acc) ->
case Tokens of
[] ->
Acc;
[{ident, T},
dot,
{ident, C},
{operator, _},
{keyword, Q},
l_paren,
{placeholder, P},
r_paren |
Rest] when ((Q =:= <<"any"/utf8>>) orelse (Q =:= <<"all"/utf8>>)) orelse (Q =:= <<"some"/utf8>>) ->
find_quantified_loop(
Rest,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C),
{some, string:lowercase(T)},
P} |
Acc]
);
[{ident, C@1},
{operator, _},
{keyword, Q@1},
l_paren,
{placeholder, P@1},
r_paren |
Rest@1] when ((Q@1 =:= <<"any"/utf8>>) orelse (Q@1 =:= <<"all"/utf8>>)) orelse (Q@1 =:= <<"some"/utf8>>) ->
find_quantified_loop(
Rest@1,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@1),
none,
P@1} |
Acc]
);
[_ | Rest@2] ->
find_quantified_loop(Rest@2, Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 528).
?DOC(false).
-spec find_quantified_patterns(list(sqlode@internal@lexer:token())) -> list(equality_match()).
find_quantified_patterns(Tokens) ->
_pipe = find_quantified_loop(Tokens, []),
lists:reverse(_pipe).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 587).
?DOC(false).
-spec find_in_loop(list(sqlode@internal@lexer:token()), list(equality_match())) -> list(equality_match()).
find_in_loop(Tokens, Acc) ->
case Tokens of
[] ->
Acc;
[{ident, T},
dot,
{ident, C},
{keyword, <<"in"/utf8>>},
l_paren,
{placeholder, P},
r_paren |
Rest] ->
find_in_loop(
Rest,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C),
{some, string:lowercase(T)},
P} |
Acc]
);
[{ident, C@1},
{keyword, <<"in"/utf8>>},
l_paren,
{placeholder, P@1},
r_paren |
Rest@1] ->
find_in_loop(
Rest@1,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@1),
none,
P@1} |
Acc]
);
[{quoted_ident, C@2},
{keyword, <<"in"/utf8>>},
l_paren,
{placeholder, P@2},
r_paren |
Rest@2] ->
find_in_loop(
Rest@2,
[{equality_match,
sqlode@internal@naming:normalize_identifier(C@2),
none,
P@2} |
Acc]
);
[_ | Rest@3] ->
find_in_loop(Rest@3, Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 519).
?DOC(false).
-spec find_in_patterns(list(sqlode@internal@lexer:token())) -> list(equality_match()).
find_in_patterns(Tokens) ->
_pipe = find_in_loop(Tokens, []),
lists:reverse(_pipe).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 667).
?DOC(false).
-spec find_type_cast_loop(
list(sqlode@internal@lexer:token()),
list(type_cast())
) -> list(type_cast()).
find_type_cast_loop(Tokens, Acc) ->
case Tokens of
[] ->
Acc;
[{placeholder, P}, {operator, <<"::"/utf8>>}, {ident, T} | Rest] ->
find_type_cast_loop(
Rest,
[{type_cast, P, string:lowercase(T)} | Acc]
);
[{placeholder, P@1}, {operator, <<"::"/utf8>>}, {keyword, T@1} | Rest@1] ->
find_type_cast_loop(Rest@1, [{type_cast, P@1, T@1} | Acc]);
[_ | Rest@2] ->
find_type_cast_loop(Rest@2, Acc)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 662).
?DOC(false).
-spec find_type_casts(list(sqlode@internal@lexer:token())) -> list(type_cast()).
find_type_casts(Tokens) ->
_pipe = find_type_cast_loop(Tokens, []),
lists:reverse(_pipe).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 715).
?DOC(false).
-spec skip_cte_body(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
skip_cte_body(Tokens) ->
case Tokens of
[] ->
[];
[{keyword, <<"select"/utf8>>} | _] = T ->
T;
[{keyword, <<"insert"/utf8>>} | _] = T@1 ->
T@1;
[{keyword, <<"update"/utf8>>} | _] = T@2 ->
T@2;
[{keyword, <<"delete"/utf8>>} | _] = T@3 ->
T@3;
[l_paren | Rest] ->
After = skip_parens(Rest, 1),
skip_cte_body(After);
[_ | Rest@1] ->
skip_cte_body(Rest@1)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 708).
?DOC(false).
-spec strip_leading_cte(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
strip_leading_cte(Tokens) ->
case Tokens of
[{keyword, <<"with"/utf8>>} | Rest] ->
skip_cte_body(Rest);
_ ->
Tokens
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 796).
?DOC(false).
-spec extract_alias_from_item(list(sqlode@internal@lexer:token())) -> gleam@option:option(binary()).
extract_alias_from_item(Tokens) ->
case lists:reverse(Tokens) of
[{ident, Name}, {keyword, <<"as"/utf8>>} | _] ->
{some, sqlode@internal@naming:normalize_identifier(Name)};
[{quoted_ident, Name@1}, {keyword, <<"as"/utf8>>} | _] ->
{some, sqlode@internal@naming:normalize_identifier(Name@1)};
_ ->
none
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 781).
?DOC(false).
-spec parse_select_items(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@query_ir:select_item()).
parse_select_items(Tokens) ->
Groups = split_on_commas(Tokens),
gleam@list:map(Groups, fun(Group) -> case Group of
[{operator, <<"*"/utf8>>}] ->
{star_item, none};
[{ident, T}, dot, {operator, <<"*"/utf8>>}] ->
{star_item, {some, string:lowercase(T)}};
_ ->
Alias = extract_alias_from_item(Group),
{expression_item, Group, Alias}
end end).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 825).
?DOC(false).
-spec parse_from_items(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@query_ir:from_item()).
parse_from_items(Tokens) ->
Groups = split_on_commas(Tokens),
gleam@list:filter_map(Groups, fun(Group) -> case Group of
[{ident, Name}] ->
{ok, {table_ref, string:lowercase(Name), none}};
[{quoted_ident, Name@1}] ->
{ok, {table_ref, string:lowercase(Name@1), none}};
[{ident, _}, dot, {ident, Name@2}] ->
{ok, {table_ref, string:lowercase(Name@2), none}};
[{ident, Name@3}, {keyword, <<"as"/utf8>>}, {ident, A}] ->
{ok,
{table_ref,
string:lowercase(Name@3),
{some, string:lowercase(A)}}};
[{ident, Name@4}, {ident, A@1}] ->
{ok,
{table_ref,
string:lowercase(Name@4),
{some, string:lowercase(A@1)}}};
_ ->
{error, nil}
end end).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 894).
?DOC(false).
-spec skip_join_keywords(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
skip_join_keywords(Tokens) ->
case Tokens of
[{keyword, Kw} | Rest] when ((((((Kw =:= <<"outer"/utf8>>) orelse (Kw =:= <<"inner"/utf8>>)) orelse (Kw =:= <<"cross"/utf8>>)) orelse (Kw =:= <<"natural"/utf8>>)) orelse (Kw =:= <<"left"/utf8>>)) orelse (Kw =:= <<"right"/utf8>>)) orelse (Kw =:= <<"full"/utf8>>) ->
skip_join_keywords(Rest);
_ ->
Tokens
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 934).
?DOC(false).
-spec read_optional_alias(list(sqlode@internal@lexer:token())) -> {gleam@option:option(binary()),
list(sqlode@internal@lexer:token())}.
read_optional_alias(Tokens) ->
case Tokens of
[{keyword, <<"as"/utf8>>}, {ident, A} | Rest] ->
{{some, string:lowercase(A)}, Rest};
[{keyword, <<"as"/utf8>>}, {quoted_ident, A@1} | Rest@1] ->
{{some, string:lowercase(A@1)}, Rest@1};
[{ident, A@2} | Rest@2] when ((((((((A@2 =/= <<"on"/utf8>>) andalso (A@2 =/= <<"where"/utf8>>)) andalso (A@2 =/= <<"group"/utf8>>)) andalso (A@2 =/= <<"order"/utf8>>)) andalso (A@2 =/= <<"limit"/utf8>>)) andalso (A@2 =/= <<"join"/utf8>>)) andalso (A@2 =/= <<"left"/utf8>>)) andalso (A@2 =/= <<"right"/utf8>>)) andalso (A@2 =/= <<"inner"/utf8>>) ->
{{some, string:lowercase(A@2)}, Rest@2};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1044).
?DOC(false).
-spec collect_until_kw_loop(
list(sqlode@internal@lexer:token()),
list(binary()),
integer(),
list(sqlode@internal@lexer:token())
) -> {list(sqlode@internal@lexer:token()), list(sqlode@internal@lexer:token())}.
collect_until_kw_loop(Tokens, Stop_keywords, Depth, Acc) ->
case Tokens of
[] ->
{lists:reverse(Acc), []};
[{keyword, Kw} | _] when Depth =:= 0 ->
case gleam@list:contains(Stop_keywords, Kw) of
true ->
{lists:reverse(Acc), Tokens};
false ->
case Tokens of
[T | Rest] ->
collect_until_kw_loop(
Rest,
Stop_keywords,
Depth,
[T | Acc]
);
_ ->
{lists:reverse(Acc), []}
end
end;
[l_paren | Rest@1] ->
collect_until_kw_loop(
Rest@1,
Stop_keywords,
Depth + 1,
[l_paren | Acc]
);
[r_paren | Rest@2] ->
collect_until_kw_loop(
Rest@2,
Stop_keywords,
Depth - 1,
[r_paren | Acc]
);
[T@1 | Rest@3] ->
collect_until_kw_loop(Rest@3, Stop_keywords, Depth, [T@1 | Acc])
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1037).
?DOC(false).
-spec collect_until_keyword(list(sqlode@internal@lexer:token()), list(binary())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
collect_until_keyword(Tokens, Stop_keywords) ->
collect_until_kw_loop(Tokens, Stop_keywords, 0, []).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 909).
?DOC(false).
-spec parse_single_join(list(sqlode@internal@lexer:token())) -> {gleam@option:option(sqlode@internal@query_ir:join_clause()),
list(sqlode@internal@lexer:token())}.
parse_single_join(Tokens) ->
{Table_name_opt, Rest} = read_table_name(Tokens),
case Table_name_opt of
none ->
{none, Rest};
{some, Name} ->
{Alias, Rest2} = read_optional_alias(Rest),
{On_tokens, Rest3} = case Rest2 of
[{keyword, <<"on"/utf8>>} | After_on] ->
{On_toks, After} = collect_until_keyword(
After_on,
[<<"join"/utf8>>,
<<"left"/utf8>>,
<<"right"/utf8>>,
<<"inner"/utf8>>,
<<"outer"/utf8>>,
<<"cross"/utf8>>,
<<"full"/utf8>>,
<<"natural"/utf8>>,
<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{{some, On_toks}, After};
_ ->
{none, Rest2}
end,
{{some, {join_clause, Name, Alias, On_tokens}}, Rest3}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 856).
?DOC(false).
-spec parse_joins_loop(
list(sqlode@internal@lexer:token()),
list(sqlode@internal@query_ir:join_clause())
) -> {list(sqlode@internal@query_ir:join_clause()),
list(sqlode@internal@lexer:token())}.
parse_joins_loop(Tokens, Acc) ->
case Tokens of
[{keyword, Kw} | Rest] when (((((((Kw =:= <<"join"/utf8>>) orelse (Kw =:= <<"left"/utf8>>)) orelse (Kw =:= <<"right"/utf8>>)) orelse (Kw =:= <<"inner"/utf8>>)) orelse (Kw =:= <<"outer"/utf8>>)) orelse (Kw =:= <<"cross"/utf8>>)) orelse (Kw =:= <<"full"/utf8>>)) orelse (Kw =:= <<"natural"/utf8>>) ->
After_join_kw = skip_join_keywords(Rest),
case After_join_kw of
[{keyword, <<"join"/utf8>>} | After_join] ->
{Clause, Remaining} = parse_single_join(After_join),
case Clause of
{some, J} ->
parse_joins_loop(Remaining, [J | Acc]);
none ->
parse_joins_loop(Remaining, Acc)
end;
_ ->
{Clause@1, Remaining@1} = parse_single_join(After_join_kw),
case Clause@1 of
{some, J@1} ->
parse_joins_loop(Remaining@1, [J@1 | Acc]);
none ->
parse_joins_loop(Remaining@1, Acc)
end
end;
_ ->
{lists:reverse(Acc), Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 850).
?DOC(false).
-spec parse_join_clauses(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@query_ir:join_clause()),
list(sqlode@internal@lexer:token())}.
parse_join_clauses(Tokens) ->
parse_joins_loop(Tokens, []).
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 806).
?DOC(false).
-spec parse_from_clause(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@query_ir:from_item()),
list(sqlode@internal@query_ir:join_clause()),
list(sqlode@internal@lexer:token())}.
parse_from_clause(Tokens) ->
case Tokens of
[{keyword, <<"from"/utf8>>} | Rest] ->
{From_tokens, After_from} = collect_until_keyword(
Rest,
[<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"join"/utf8>>,
<<"left"/utf8>>,
<<"right"/utf8>>,
<<"inner"/utf8>>,
<<"outer"/utf8>>,
<<"cross"/utf8>>,
<<"full"/utf8>>,
<<"natural"/utf8>>,
<<"lateral"/utf8>>]
),
From_items = parse_from_items(From_tokens),
{Joins, After_joins} = parse_join_clauses(After_from),
{From_items, Joins, After_joins};
_ ->
{[], [], Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1077).
?DOC(false).
-spec extract_clause(
list(sqlode@internal@lexer:token()),
binary(),
list(binary())
) -> {gleam@option:option(list(sqlode@internal@lexer:token())),
list(sqlode@internal@lexer:token())}.
extract_clause(Tokens, Keyword, Stop_keywords) ->
case Tokens of
[{keyword, Kw} | Rest] when Kw =:= Keyword ->
After_kw = case {Kw, Rest} of
{<<"group"/utf8>>, [{keyword, <<"by"/utf8>>} | R]} ->
R;
{<<"order"/utf8>>, [{keyword, <<"by"/utf8>>} | R@1]} ->
R@1;
{_, _} ->
Rest
end,
{Clause_tokens, Remaining} = collect_until_keyword(
After_kw,
Stop_keywords
),
case Clause_tokens of
[] ->
{none, Remaining};
_ ->
{{some, Clause_tokens}, Remaining}
end;
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 733).
?DOC(false).
-spec structure_select(list(sqlode@internal@lexer:token())) -> sqlode@internal@query_ir:sql_statement().
structure_select(Tokens) ->
After_select = case Tokens of
[{keyword, <<"select"/utf8>>}, {keyword, <<"distinct"/utf8>>} | Rest] ->
Rest;
[{keyword, <<"select"/utf8>>} | Rest@1] ->
Rest@1;
_ ->
Tokens
end,
{Select_tokens, Rest_after_select} = collect_until_keyword(
After_select,
[<<"from"/utf8>>,
<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
Select_items = parse_select_items(Select_tokens),
{From_items, Joins, Rest_after_from} = parse_from_clause(Rest_after_select),
{Where_tokens, Rest_after_where} = extract_clause(
Rest_after_from,
<<"where"/utf8>>,
[<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{Group_by_tokens, Rest_after_group} = extract_clause(
Rest_after_where,
<<"group"/utf8>>,
[<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{Having_tokens, Rest_after_having} = extract_clause(
Rest_after_group,
<<"having"/utf8>>,
[<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{Order_by_tokens, Rest_after_order} = extract_clause(
Rest_after_having,
<<"order"/utf8>>,
[<<"limit"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{Limit_tokens, _} = extract_clause(
Rest_after_order,
<<"limit"/utf8>>,
[<<"union"/utf8>>, <<"intersect"/utf8>>, <<"except"/utf8>>]
),
{select_statement,
Select_items,
From_items,
Joins,
Where_tokens,
Group_by_tokens,
Having_tokens,
Order_by_tokens,
Limit_tokens}.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1101).
?DOC(false).
-spec skip_to_keyword(list(sqlode@internal@lexer:token()), binary()) -> gleam@option:option(list(sqlode@internal@lexer:token())).
skip_to_keyword(Tokens, Keyword) ->
case Tokens of
[] ->
none;
[{keyword, Kw} | Rest] when Kw =:= Keyword ->
{some, Rest};
[_ | Rest@1] ->
skip_to_keyword(Rest@1, Keyword)
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1112).
?DOC(false).
-spec extract_returning_tokens(list(sqlode@internal@lexer:token())) -> gleam@option:option(list(sqlode@internal@lexer:token())).
extract_returning_tokens(Tokens) ->
case skip_to_keyword(Tokens, <<"returning"/utf8>>) of
{some, After_returning} ->
case After_returning of
[] ->
none;
Toks ->
{some, Toks}
end;
none ->
none
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 963).
?DOC(false).
-spec structure_insert(list(sqlode@internal@lexer:token())) -> sqlode@internal@query_ir:sql_statement().
structure_insert(Tokens) ->
case find_insert_parts(Tokens) of
{some, Parts} ->
Returning = extract_returning_tokens(Tokens),
{insert_statement,
erlang:element(2, Parts),
erlang:element(3, Parts),
erlang:element(4, Parts),
Returning};
none ->
{unstructured_statement, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 980).
?DOC(false).
-spec structure_update(list(sqlode@internal@lexer:token())) -> sqlode@internal@query_ir:sql_statement().
structure_update(Tokens) ->
case Tokens of
[{keyword, <<"update"/utf8>>} | Rest] ->
{Table_name_opt, After_table} = read_table_name(Rest),
case Table_name_opt of
none ->
{unstructured_statement, Tokens};
{some, Name} ->
{Set_tokens, After_set} = case skip_to_keyword(
After_table,
<<"set"/utf8>>
) of
{some, After_set_kw} ->
collect_until_keyword(
After_set_kw,
[<<"where"/utf8>>,
<<"returning"/utf8>>,
<<"from"/utf8>>]
);
none ->
{[], After_table}
end,
{Where_tokens, _} = extract_clause(
After_set,
<<"where"/utf8>>,
[<<"returning"/utf8>>]
),
Returning = extract_returning_tokens(Tokens),
{update_statement,
Name,
Set_tokens,
Where_tokens,
Returning}
end;
_ ->
{unstructured_statement, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 1012).
?DOC(false).
-spec structure_delete(list(sqlode@internal@lexer:token())) -> sqlode@internal@query_ir:sql_statement().
structure_delete(Tokens) ->
case Tokens of
[{keyword, <<"delete"/utf8>>}, {keyword, <<"from"/utf8>>} | Rest] ->
{Table_name_opt, After_table} = read_table_name(Rest),
case Table_name_opt of
none ->
{unstructured_statement, Tokens};
{some, Name} ->
{Where_tokens, _} = extract_clause(
After_table,
<<"where"/utf8>>,
[<<"returning"/utf8>>]
),
Returning = extract_returning_tokens(Tokens),
{delete_statement, Name, Where_tokens, Returning}
end;
_ ->
{unstructured_statement, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/token_utils.gleam", 697).
?DOC(false).
-spec structure_tokens(list(sqlode@internal@lexer:token())) -> sqlode@internal@query_ir:sql_statement().
structure_tokens(Tokens) ->
Stripped = strip_leading_cte(Tokens),
case Stripped of
[{keyword, <<"select"/utf8>>} | _] ->
structure_select(Stripped);
[{keyword, <<"insert"/utf8>>} | _] ->
structure_insert(Stripped);
[{keyword, <<"update"/utf8>>} | _] ->
structure_update(Stripped);
[{keyword, <<"delete"/utf8>>} | _] ->
structure_delete(Stripped);
_ ->
{unstructured_statement, Stripped}
end.