Current section
Files
Jump to
Current section
Files
src/sqlode@internal@query_analyzer@expr_parser.erl
-module(sqlode@internal@query_analyzer@expr_parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/internal/query_analyzer/expr_parser.gleam").
-export([parse_stmt/2, parse_select_core/2, parse_expr/2]).
-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/expr_parser.gleam", 76).
?DOC(false).
-spec attach_recursive(list(sqlode@internal@query_ir:cte_def()), boolean()) -> list(sqlode@internal@query_ir:cte_def()).
attach_recursive(Ctes, Recursive) ->
case Recursive of
false ->
Ctes;
true ->
gleam@list:map(
Ctes,
fun(Cte) ->
{cte_def,
erlang:element(2, Cte),
erlang:element(3, Cte),
erlang:element(4, Cte),
true}
end
)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 439).
?DOC(false).
-spec is_expr_end(sqlode@internal@lexer:token()) -> boolean().
is_expr_end(Token) ->
case Token of
r_paren ->
true;
{ident, _} ->
true;
{quoted_ident, _} ->
true;
{string_lit, _} ->
true;
{number_lit, _} ->
true;
{placeholder, _} ->
true;
star ->
true;
_ ->
false
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 412).
?DOC(false).
-spec split_trailing_alias(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
gleam@option:option(binary())}.
split_trailing_alias(Tokens) ->
case lists:reverse(Tokens) of
[{ident, Name}, {keyword, <<"as"/utf8>>} | Rest] ->
{lists:reverse(Rest),
{some, sqlode@internal@naming:normalize_identifier(Name)}};
[{quoted_ident, Name@1}, {keyword, <<"as"/utf8>>} | Rest@1] ->
{lists:reverse(Rest@1),
{some, sqlode@internal@naming:normalize_identifier(Name@1)}};
[{ident, Name@2}, Prev | Rest@2] ->
case is_expr_end(Prev) of
true ->
{lists:reverse([Prev | Rest@2]),
{some,
sqlode@internal@naming:normalize_identifier(Name@2)}};
false ->
{Tokens, none}
end;
_ ->
{Tokens, none}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 641).
?DOC(false).
-spec is_reserved_after_table(binary()) -> boolean().
is_reserved_after_table(Name) ->
Lowered = string:lowercase(Name),
gleam@list:contains(
[<<"on"/utf8>>,
<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"returning"/utf8>>,
<<"using"/utf8>>,
<<"lateral"/utf8>>,
<<"left"/utf8>>,
<<"right"/utf8>>,
<<"inner"/utf8>>,
<<"outer"/utf8>>,
<<"cross"/utf8>>,
<<"full"/utf8>>,
<<"natural"/utf8>>,
<<"join"/utf8>>,
<<"window"/utf8>>],
Lowered
).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 619).
?DOC(false).
-spec parse_optional_alias(list(sqlode@internal@lexer:token())) -> {gleam@option:option(binary()),
list(sqlode@internal@lexer:token())}.
parse_optional_alias(Tokens) ->
case Tokens of
[{keyword, <<"as"/utf8>>}, {ident, Name} | Rest] ->
{{some, string:lowercase(Name)}, Rest};
[{keyword, <<"as"/utf8>>}, {quoted_ident, Name@1} | Rest@1] ->
{{some, string:lowercase(Name@1)}, Rest@1};
[{ident, Name@2} | Rest@2] ->
case is_reserved_after_table(Name@2) of
true ->
{none, Tokens};
false ->
{{some, string:lowercase(Name@2)}, Rest@2}
end;
[{quoted_ident, Name@3} | Rest@3] ->
{{some, string:lowercase(Name@3)}, Rest@3};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 679).
?DOC(false).
-spec detect_join(list(sqlode@internal@lexer:token())) -> gleam@option:option({sqlode@internal@query_ir:join_kind(),
list(sqlode@internal@lexer:token()),
boolean()}).
detect_join(Tokens) ->
case Tokens of
[{keyword, <<"join"/utf8>>}, {keyword, <<"lateral"/utf8>>} | Rest] ->
{some, {inner_join, Rest, true}};
[{keyword, <<"join"/utf8>>} | Rest@1] ->
{some, {inner_join, Rest@1, false}};
[{keyword, <<"inner"/utf8>>}, {keyword, <<"join"/utf8>>} | Rest@2] ->
{some, {inner_join, Rest@2, false}};
[{keyword, <<"cross"/utf8>>}, {keyword, <<"join"/utf8>>} | Rest@3] ->
{some, {cross_join, Rest@3, false}};
[{keyword, <<"left"/utf8>>},
{keyword, <<"join"/utf8>>},
{keyword, <<"lateral"/utf8>>} |
Rest@4] ->
{some, {left_join, Rest@4, true}};
[{keyword, <<"left"/utf8>>},
{keyword, <<"outer"/utf8>>},
{keyword, <<"join"/utf8>>},
{keyword, <<"lateral"/utf8>>} |
Rest@5] ->
{some, {left_join, Rest@5, true}};
[{keyword, <<"left"/utf8>>},
{keyword, <<"outer"/utf8>>},
{keyword, <<"join"/utf8>>} |
Rest@6] ->
{some, {left_join, Rest@6, false}};
[{keyword, <<"left"/utf8>>}, {keyword, <<"join"/utf8>>} | Rest@7] ->
{some, {left_join, Rest@7, false}};
[{keyword, <<"right"/utf8>>},
{keyword, <<"outer"/utf8>>},
{keyword, <<"join"/utf8>>} |
Rest@8] ->
{some, {right_join, Rest@8, false}};
[{keyword, <<"right"/utf8>>}, {keyword, <<"join"/utf8>>} | Rest@9] ->
{some, {right_join, Rest@9, false}};
[{keyword, <<"full"/utf8>>},
{keyword, <<"outer"/utf8>>},
{keyword, <<"join"/utf8>>} |
Rest@10] ->
{some, {full_join, Rest@10, false}};
[{keyword, <<"full"/utf8>>}, {keyword, <<"join"/utf8>>} | Rest@11] ->
{some, {full_join, Rest@11, false}};
_ ->
none
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1283).
?DOC(false).
-spec detect_slice_macro(list(sqlode@internal@lexer:token())) -> gleam@option:option(binary()).
detect_slice_macro(Tokens) ->
case Tokens of
[{ident, S}, dot, {ident, Sl}, l_paren, {ident, Name}, r_paren] ->
case {string:lowercase(S), string:lowercase(Sl)} of
{<<"sqlode"/utf8>>, <<"slice"/utf8>>} ->
{some, string:lowercase(Name)};
{_, _} ->
none
end;
_ ->
none
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1487).
?DOC(false).
-spec render_type_parens(list(sqlode@internal@lexer:token())) -> binary().
render_type_parens(Tokens) ->
_pipe = Tokens,
_pipe@1 = gleam@list:map(_pipe, fun(T) -> case T of
{ident, N} ->
N;
{keyword, K} ->
K;
{number_lit, N@1} ->
N@1;
comma ->
<<","/utf8>>;
_ ->
<<""/utf8>>
end end),
gleam@string:join(_pipe@1, <<""/utf8>>).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1845).
?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);
[T | Rest@3] ->
split_commas_loop(Rest@3, Depth, [T | Current], Acc)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1841).
?DOC(false).
-spec split_on_top_commas(list(sqlode@internal@lexer:token())) -> list(list(sqlode@internal@lexer:token())).
split_on_top_commas(Tokens) ->
split_commas_loop(Tokens, 0, [], []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 173).
?DOC(false).
-spec parse_ident_list(list(sqlode@internal@lexer:token())) -> list(binary()).
parse_ident_list(Tokens) ->
_pipe = Tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:filter_map(_pipe@1, fun(Group) -> case Group of
[{ident, N}] ->
{ok, string:lowercase(N)};
[{quoted_ident, N@1}] ->
{ok, string:lowercase(N@1)};
_ ->
{error, nil}
end end).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1876).
?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;
[T | Rest@2] ->
collect_paren_loop(Rest@2, Depth, [T | Acc])
end
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1870).
?DOC(false).
-spec collect_parens(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
collect_parens(Tokens) ->
collect_paren_loop(Tokens, 1, []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 593).
?DOC(false).
-spec parse_aliased_column_list(list(sqlode@internal@lexer:token())) -> {gleam@option:option(binary()),
list(binary()),
list(sqlode@internal@lexer:token())}.
parse_aliased_column_list(Tokens) ->
After_as = case Tokens of
[{keyword, <<"as"/utf8>>} | Rest] ->
Rest;
_ ->
Tokens
end,
case After_as of
[{ident, Alias}, l_paren | After_lp] ->
{Cols, After} = collect_parens(After_lp),
{{some, string:lowercase(Alias)}, parse_ident_list(Cols), After};
[{quoted_ident, Alias@1}, l_paren | After_lp@1] ->
{Cols@1, After@1} = collect_parens(After_lp@1),
{{some, string:lowercase(Alias@1)},
parse_ident_list(Cols@1),
After@1};
[{ident, Alias@2} | Rest@1] ->
{{some, string:lowercase(Alias@2)}, [], Rest@1};
[{quoted_ident, Alias@3} | Rest@2] ->
{{some, string:lowercase(Alias@3)}, [], Rest@2};
_ ->
{none, [], After_as}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 569).
?DOC(false).
-spec parse_subquery_alias(
sqlode@internal@query_ir:select_core(),
list(sqlode@internal@lexer:token()),
boolean()
) -> {sqlode@internal@query_ir:from_item_ex(),
list(sqlode@internal@lexer:token())}.
parse_subquery_alias(Core, After, Lateral) ->
{Alias_opt, Col_aliases, Rest} = parse_aliased_column_list(After),
Alias = gleam@option:unwrap(Alias_opt, <<""/utf8>>),
Item = {from_subquery, Core, Alias, Col_aliases},
case Lateral of
true ->
{{from_join,
{from_table, <<""/utf8>>, none},
Item,
cross_join,
join_no_condition,
true},
Rest};
false ->
{Item, Rest}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 874).
?DOC(false).
-spec skip_values_rows(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
skip_values_rows(Tokens) ->
case Tokens of
[l_paren | Rest] ->
{_, After} = collect_parens(Rest),
case After of
[comma | More] ->
skip_values_rows(More);
_ ->
After
end;
_ ->
Tokens
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1460).
?DOC(false).
-spec collect_type_tokens(list(sqlode@internal@lexer:token()), list(binary())) -> {binary(),
list(sqlode@internal@lexer:token())}.
collect_type_tokens(Tokens, Acc) ->
case Tokens of
[{ident, N} | Rest] ->
collect_type_tokens(Rest, [string:lowercase(N) | Acc]);
[{keyword, K} | Rest@1] ->
collect_type_tokens(Rest@1, [K | Acc]);
[l_paren | Rest@2] ->
{Inside, After} = collect_parens(Rest@2),
Inside_text = render_type_parens(Inside),
Last_acc = case Acc of
[First | Rest_acc] ->
[<<<<<<First/binary, "("/utf8>>/binary, Inside_text/binary>>/binary,
")"/utf8>> |
Rest_acc];
[] ->
[<<<<"("/utf8, Inside_text/binary>>/binary, ")"/utf8>>]
end,
collect_type_tokens(After, Last_acc);
[{operator, <<"[]"/utf8>>} | Rest@3] ->
case Acc of
[First@1 | Rest_acc@1] ->
collect_type_tokens(
Rest@3,
[<<First@1/binary, "[]"/utf8>> | Rest_acc@1]
);
[] ->
collect_type_tokens(Rest@3, [<<"[]"/utf8>>])
end;
_ ->
{begin
_pipe = lists:reverse(Acc),
_pipe@1 = gleam@string:join(_pipe, <<" "/utf8>>),
gleam@string:trim(_pipe@1)
end,
Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1456).
?DOC(false).
-spec read_type_tokens(list(sqlode@internal@lexer:token())) -> {binary(),
list(sqlode@internal@lexer:token())}.
read_type_tokens(Tokens) ->
collect_type_tokens(Tokens, []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1443).
?DOC(false).
-spec parse_cast_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token())
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_cast_tail(Atom, Tokens) ->
case Tokens of
[{operator, <<"::"/utf8>>} | Rest] ->
{Type_name, After} = read_type_tokens(Rest),
parse_cast_tail({cast, Atom, Type_name}, After);
_ ->
{Atom, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1694).
?DOC(false).
-spec collect_parens_after_where(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
collect_parens_after_where(Tokens) ->
collect_parens(Tokens).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1904).
?DOC(false).
-spec collect_bracket_loop(
list(sqlode@internal@lexer:token()),
integer(),
list(sqlode@internal@lexer:token())
) -> {list(sqlode@internal@lexer:token()), list(sqlode@internal@lexer:token())}.
collect_bracket_loop(Tokens, Depth, Acc) ->
case Depth =< 0 of
true ->
{lists:reverse(Acc), Tokens};
false ->
case Tokens of
[] ->
{lists:reverse(Acc), []};
[{operator, <<"["/utf8>>} | Rest] ->
collect_bracket_loop(
Rest,
Depth + 1,
[{operator, <<"["/utf8>>} | Acc]
);
[{operator, <<"]"/utf8>>} | Rest@1] ->
case Depth =:= 1 of
true ->
{lists:reverse(Acc), Rest@1};
false ->
collect_bracket_loop(
Rest@1,
Depth - 1,
[{operator, <<"]"/utf8>>} | Acc]
)
end;
[T | Rest@2] ->
collect_bracket_loop(Rest@2, Depth, [T | Acc])
end
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1898).
?DOC(false).
-spec collect_brackets(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
collect_brackets(Tokens) ->
collect_bracket_loop(Tokens, 1, []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1934).
?DOC(false).
-spec collect_until_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_loop(Tokens, Stop, Depth, Acc) ->
case Tokens of
[] ->
{lists:reverse(Acc), []};
[l_paren | Rest] ->
collect_until_loop(Rest, Stop, Depth + 1, [l_paren | Acc]);
[r_paren | Rest@1] ->
collect_until_loop(Rest@1, Stop, Depth - 1, [r_paren | Acc]);
[{keyword, Kw} | Rest@2] = T when Depth =:= 0 ->
case gleam@list:contains(Stop, Kw) of
true ->
{lists:reverse(Acc), T};
false ->
collect_until_loop(
Rest@2,
Stop,
Depth,
[{keyword, Kw} | Acc]
)
end;
[T@1 | Rest@3] ->
collect_until_loop(Rest@3, Stop, Depth, [T@1 | Acc])
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1927).
?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) ->
collect_until_loop(Tokens, Stop, 0, []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1957).
?DOC(false).
-spec read_qualified_name(list(sqlode@internal@lexer:token())) -> {binary(),
list(sqlode@internal@lexer:token())}.
read_qualified_name(Tokens) ->
case Tokens of
[{ident, _}, dot, {ident, Name} | Rest] ->
{string:lowercase(Name), Rest};
[{ident, Name@1} | Rest@1] ->
{string:lowercase(Name@1), Rest@1};
[{quoted_ident, Name@2} | Rest@2] ->
{string:lowercase(Name@2), Rest@2};
_ ->
{<<""/utf8>>, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1971).
?DOC(false).
-spec drop_first(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
drop_first(Tokens) ->
case Tokens of
[_ | Rest] ->
Rest;
[] ->
[]
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1978).
?DOC(false).
-spec empty_core() -> sqlode@internal@query_ir:select_core().
empty_core() ->
{select_core, false, [], [], none, [], none, [], none, none, none}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1993).
?DOC(false).
-spec trim_noise(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
trim_noise(Tokens) ->
gleam@list:filter(Tokens, fun(T) -> case T of
semicolon ->
false;
_ ->
true
end end).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 2008).
?DOC(false).
-spec split_on_as_loop(
list(sqlode@internal@lexer:token()),
integer(),
list(sqlode@internal@lexer:token())
) -> {list(sqlode@internal@lexer:token()), list(sqlode@internal@lexer:token())}.
split_on_as_loop(Tokens, Depth, Acc) ->
case Tokens of
[] ->
{lists:reverse(Acc), []};
[l_paren | Rest] ->
split_on_as_loop(Rest, Depth + 1, [l_paren | Acc]);
[r_paren | Rest@1] ->
split_on_as_loop(Rest@1, Depth - 1, [r_paren | Acc]);
[{keyword, <<"as"/utf8>>} | Rest@2] when Depth =:= 0 ->
{lists:reverse(Acc), Rest@2};
[T | Rest@3] ->
split_on_as_loop(Rest@3, Depth, [T | Acc])
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 2002).
?DOC(false).
-spec split_on_as(list(sqlode@internal@lexer:token())) -> {list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token())}.
split_on_as(Tokens) ->
split_on_as_loop(Tokens, 0, []).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 2024).
?DOC(false).
-spec skip_to_returning_or_end(list(sqlode@internal@lexer:token())) -> list(sqlode@internal@lexer:token()).
skip_to_returning_or_end(Tokens) ->
case Tokens of
[] ->
[];
[{keyword, <<"returning"/utf8>>} | _] = T ->
T;
[_ | Rest] ->
skip_to_returning_or_end(Rest)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 2032).
?DOC(false).
-spec decode_placeholder(binary()) -> integer().
decode_placeholder(Raw) ->
Digits = case gleam@string:to_graphemes(Raw) of
[] ->
<<""/utf8>>;
[_ | Rest] ->
erlang:list_to_binary(Rest)
end,
case gleam_stdlib:parse_int(Digits) of
{ok, N} ->
N;
{error, _} ->
0
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 143).
?DOC(false).
-spec parse_cte_after_name(
binary(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> gleam@option:option({sqlode@internal@query_ir:cte_def(),
list(sqlode@internal@lexer:token())}).
parse_cte_after_name(Name, Tokens, Engine) ->
{Columns, After_cols} = case Tokens of
[l_paren | After_lp] ->
{Inside, After} = collect_parens(After_lp),
{parse_ident_list(Inside), After};
_ ->
{[], Tokens}
end,
case After_cols of
[{keyword, <<"as"/utf8>>}, l_paren | After_as_lp] ->
{Inner, After@1} = collect_parens(After_as_lp),
Body = parse_stmt(Inner, Engine),
{some,
{{cte_def, string:lowercase(Name), Columns, Body, false},
After@1}};
_ ->
none
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 52).
?DOC(false).
-spec parse_stmt(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:stmt().
parse_stmt(Tokens, Engine) ->
{Ctes, Recursive, After_ctes} = parse_with_clause(Tokens, Engine),
case After_ctes of
[{keyword, <<"select"/utf8>>} | _] ->
{Core, _} = parse_select_core(After_ctes, Engine),
{select_stmt, attach_recursive(Ctes, Recursive), Core};
[{keyword, <<"insert"/utf8>>} | Rest] ->
parse_insert_body(attach_recursive(Ctes, Recursive), Rest, Engine);
[{keyword, <<"update"/utf8>>} | Rest@1] ->
parse_update_body(attach_recursive(Ctes, Recursive), Rest@1, Engine);
[{keyword, <<"delete"/utf8>>} | Rest@2] ->
parse_delete_body(attach_recursive(Ctes, Recursive), Rest@2, Engine);
_ ->
{unstructured_stmt,
<<"unrecognised top-level statement"/utf8>>,
Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 98).
?DOC(false).
-spec parse_with_clause(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {list(sqlode@internal@query_ir:cte_def()),
boolean(),
list(sqlode@internal@lexer:token())}.
parse_with_clause(Tokens, Engine) ->
case Tokens of
[{keyword, <<"with"/utf8>>}, {keyword, <<"recursive"/utf8>>} | Rest] ->
{Ctes, Remaining} = parse_cte_list(Rest, [], Engine),
{Ctes, true, Remaining};
[{keyword, <<"with"/utf8>>} | Rest@1] ->
{Ctes@1, Remaining@1} = parse_cte_list(Rest@1, [], Engine),
{Ctes@1, false, Remaining@1};
_ ->
{[], false, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 115).
?DOC(false).
-spec parse_cte_list(
list(sqlode@internal@lexer:token()),
list(sqlode@internal@query_ir:cte_def()),
sqlode@internal@model:engine()
) -> {list(sqlode@internal@query_ir:cte_def()),
list(sqlode@internal@lexer:token())}.
parse_cte_list(Tokens, Acc, Engine) ->
case parse_single_cte(Tokens, Engine) of
{some, {Cte, After}} ->
case After of
[comma | More] ->
parse_cte_list(More, [Cte | Acc], Engine);
_ ->
{lists:reverse([Cte | Acc]), After}
end;
none ->
{lists:reverse(Acc), Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 130).
?DOC(false).
-spec parse_single_cte(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> gleam@option:option({sqlode@internal@query_ir:cte_def(),
list(sqlode@internal@lexer:token())}).
parse_single_cte(Tokens, Engine) ->
case Tokens of
[{ident, Name} | After_name] ->
parse_cte_after_name(Name, After_name, Engine);
[{quoted_ident, Name@1} | After_name@1] ->
parse_cte_after_name(Name@1, After_name@1, Engine);
_ ->
none
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 319).
?DOC(false).
-spec parse_set_op(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {gleam@option:option(sqlode@internal@query_ir:set_op()),
list(sqlode@internal@lexer:token())}.
parse_set_op(Tokens, Engine) ->
case Tokens of
[{keyword, Kw} | Rest] when ((Kw =:= <<"union"/utf8>>) orelse (Kw =:= <<"intersect"/utf8>>)) orelse (Kw =:= <<"except"/utf8>>) ->
{All, Rest2} = case Rest of
[{keyword, <<"all"/utf8>>} | More] ->
{true, More};
_ ->
{false, Rest}
end,
{Core, After} = parse_select_core(Rest2, Engine),
Kind = case Kw of
<<"union"/utf8>> ->
union;
<<"intersect"/utf8>> ->
intersect;
_ ->
except
end,
{{some, {set_op, Kind, All, Core}}, After};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 189).
?DOC(false).
-spec parse_select_core(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:select_core(),
list(sqlode@internal@lexer:token())}.
parse_select_core(Tokens, Engine) ->
{Distinct, After_distinct} = case Tokens of
[{keyword, <<"select"/utf8>>}, {keyword, <<"distinct"/utf8>>} | Rest] ->
{true, Rest};
[{keyword, <<"select"/utf8>>} | Rest@1] ->
{false, Rest@1};
_ ->
{false, Tokens}
end,
{Select_tokens, Rest_after_select} = collect_until_keyword(
After_distinct,
[<<"from"/utf8>>,
<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"window"/utf8>>]
),
Items = parse_select_items(Select_tokens, Engine),
{From_list, Rest_after_from} = case Rest_after_select of
[{keyword, <<"from"/utf8>>} | Rest@2] ->
parse_from_clause(Rest@2, Engine);
_ ->
{[], Rest_after_select}
end,
{Where_expr, Rest_after_where} = case Rest_after_from of
[{keyword, <<"where"/utf8>>} | Rest@3] ->
{Where_toks, After} = collect_until_keyword(
Rest@3,
[<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"window"/utf8>>]
),
{{some, parse_expr(Where_toks, Engine)}, After};
_ ->
{none, Rest_after_from}
end,
{Group_by, Rest_after_group} = case Rest_after_where of
[{keyword, <<"group"/utf8>>}, {keyword, <<"by"/utf8>>} | Rest@4] ->
{Gb_toks, After@1} = collect_until_keyword(
Rest@4,
[<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"window"/utf8>>]
),
{parse_expr_list(Gb_toks, Engine), After@1};
_ ->
{[], Rest_after_where}
end,
{Having_expr, Rest_after_having} = case Rest_after_group of
[{keyword, <<"having"/utf8>>} | Rest@5] ->
{H_toks, After@2} = collect_until_keyword(
Rest@5,
[<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"window"/utf8>>]
),
{{some, parse_expr(H_toks, Engine)}, After@2};
_ ->
{none, Rest_after_group}
end,
Rest_after_window = case Rest_after_having of
[{keyword, <<"window"/utf8>>} | Rest@6] ->
{_, After@3} = collect_until_keyword(
Rest@6,
[<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
After@3;
_ ->
Rest_after_having
end,
{Order_by, Rest_after_order} = case Rest_after_window of
[{keyword, <<"order"/utf8>>}, {keyword, <<"by"/utf8>>} | Rest@7] ->
{O_toks, After@4} = collect_until_keyword(
Rest@7,
[<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
{parse_order_keys(O_toks, Engine), After@4};
_ ->
{[], Rest_after_window}
end,
{Limit, Offset_from_limit, Rest_after_limit} = case Rest_after_order of
[{keyword, <<"limit"/utf8>>} | Rest@8] ->
{L_toks, After@5} = collect_until_keyword(
Rest@8,
[<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>]
),
case Engine of
my_s_q_l ->
case split_on_top_commas(L_toks) of
[Offset_toks, Count_toks] ->
{{some, parse_expr(Count_toks, Engine)},
{some, parse_expr(Offset_toks, Engine)},
After@5};
_ ->
{{some, parse_expr(L_toks, Engine)}, none, After@5}
end;
_ ->
{{some, parse_expr(L_toks, Engine)}, none, After@5}
end;
_ ->
{none, none, Rest_after_order}
end,
{Offset, Rest_after_offset} = case Rest_after_limit of
[{keyword, <<"offset"/utf8>>} | Rest@9] ->
{O_toks@1, After@6} = collect_until_keyword(
Rest@9,
[<<"union"/utf8>>, <<"intersect"/utf8>>, <<"except"/utf8>>]
),
{{some, parse_expr(O_toks@1, Engine)}, After@6};
_ ->
{Offset_from_limit, Rest_after_limit}
end,
{Set_op, Remaining} = parse_set_op(Rest_after_offset, Engine),
{{select_core,
Distinct,
Items,
From_list,
Where_expr,
Group_by,
Having_expr,
Order_by,
Limit,
Offset,
Set_op},
Remaining}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1723).
?DOC(false).
-spec parse_window_spec(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:window_spec().
parse_window_spec(Tokens, Engine) ->
{_, Rest@1} = case Tokens of
[{ident, _} | Rest] ->
{true, Rest};
_ ->
{false, Tokens}
end,
{Partition_by, Rest2} = case Rest@1 of
[{keyword, <<"partition"/utf8>>}, {keyword, <<"by"/utf8>>} | More] ->
{P_toks, After} = collect_until_keyword(
More,
[<<"order"/utf8>>,
<<"range"/utf8>>,
<<"rows"/utf8>>,
<<"groups"/utf8>>]
),
{parse_expr_list(P_toks, Engine), After};
_ ->
{[], Rest@1}
end,
{Order_by, Rest3} = case Rest2 of
[{keyword, <<"order"/utf8>>}, {keyword, <<"by"/utf8>>} | More@1] ->
{O_toks, After@1} = collect_until_keyword(
More@1,
[<<"range"/utf8>>, <<"rows"/utf8>>, <<"groups"/utf8>>]
),
{parse_order_keys(O_toks, Engine), After@1};
_ ->
{[], Rest2}
end,
Frame = case Rest3 of
[] ->
none;
Other ->
{some, Other}
end,
{window_spec, Partition_by, Order_by, Frame}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 343).
?DOC(false).
-spec parse_order_keys(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(sqlode@internal@query_ir:order_key()).
parse_order_keys(Tokens, Engine) ->
_pipe = Tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:map(_pipe@1, fun(Group) -> parse_order_key(Group, Engine) end).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 352).
?DOC(false).
-spec parse_order_key(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:order_key().
parse_order_key(Tokens, Engine) ->
Reversed = lists:reverse(Tokens),
{Nulls, Reversed2} = case Reversed of
[{keyword, <<"first"/utf8>>}, {keyword, <<"nulls"/utf8>>} | Rest] ->
{{some, nulls_first}, Rest};
[{keyword, <<"last"/utf8>>}, {keyword, <<"nulls"/utf8>>} | Rest@1] ->
{{some, nulls_last}, Rest@1};
_ ->
{none, Reversed}
end,
{Descending, Reversed3} = case Reversed2 of
[{keyword, <<"desc"/utf8>>} | Rest@2] ->
{true, Rest@2};
[{keyword, <<"asc"/utf8>>} | Rest@3] ->
{false, Rest@3};
_ ->
{false, Reversed2}
end,
Expr_tokens = lists:reverse(Reversed3),
{order_key, parse_expr(Expr_tokens, Engine), Descending, Nulls}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1023).
?DOC(false).
-spec parse_expr(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:expr().
parse_expr(Tokens, Engine) ->
{Expr, Rest} = parse_or(trim_noise(Tokens), Engine),
case Rest of
[] ->
Expr;
_ ->
{raw_expr, <<"trailing tokens after expression"/utf8>>, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1047).
?DOC(false).
-spec parse_or(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_or(Tokens, Engine) ->
{Left, Rest} = parse_and(Tokens, Engine),
parse_or_tail(Left, Rest, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1055).
?DOC(false).
-spec parse_or_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_or_tail(Left, Tokens, Engine) ->
case Tokens of
[{keyword, <<"or"/utf8>>} | Rest] ->
{Right, After} = parse_and(Rest, Engine),
parse_or_tail({binary, <<"or"/utf8>>, Left, Right}, After, Engine);
_ ->
{Left, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1073).
?DOC(false).
-spec parse_and(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_and(Tokens, Engine) ->
{Left, Rest} = parse_not(Tokens, Engine),
parse_and_tail(Left, Rest, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1081).
?DOC(false).
-spec parse_and_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_and_tail(Left, Tokens, Engine) ->
case Tokens of
[{keyword, <<"and"/utf8>>} | Rest] ->
{Right, After} = parse_not(Rest, Engine),
parse_and_tail({binary, <<"and"/utf8>>, Left, Right}, After, Engine);
_ ->
{Left, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1099).
?DOC(false).
-spec parse_not(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_not(Tokens, Engine) ->
case Tokens of
[{keyword, <<"not"/utf8>>} | Rest] ->
{Inner, After} = parse_not(Rest, Engine),
{{unary, <<"not"/utf8>>, Inner}, After};
_ ->
parse_comparison(Tokens, Engine)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1112).
?DOC(false).
-spec parse_comparison(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_comparison(Tokens, Engine) ->
{Left, Rest} = parse_additive(Tokens, Engine),
parse_comparison_tail(Left, Rest, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1120).
?DOC(false).
-spec parse_comparison_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_comparison_tail(Left, Tokens, Engine) ->
case Tokens of
[{operator, Op} | Rest] when (((((((((((Op =:= <<"="/utf8>>) orelse (Op =:= <<"<>"/utf8>>)) orelse (Op =:= <<"!="/utf8>>)) orelse (Op =:= <<"<"/utf8>>)) orelse (Op =:= <<">"/utf8>>)) orelse (Op =:= <<"<="/utf8>>)) orelse (Op =:= <<">="/utf8>>)) orelse (Op =:= <<"@>"/utf8>>)) orelse (Op =:= <<"<@"/utf8>>)) orelse (Op =:= <<"?|"/utf8>>)) orelse (Op =:= <<"?&"/utf8>>)) orelse (Op =:= <<"&&"/utf8>>) ->
case Rest of
[{keyword, <<"any"/utf8>>}, l_paren | After_lp] ->
{Inner, After} = collect_parens(After_lp),
Right = parse_expr(Inner, Engine),
{{quantified, Op, Left, q_any, Right}, After};
[{keyword, <<"all"/utf8>>}, l_paren | After_lp@1] ->
{Inner@1, After@1} = collect_parens(After_lp@1),
Right@1 = parse_expr(Inner@1, Engine),
{{quantified, Op, Left, q_all, Right@1}, After@1};
_ ->
{Right@2, After@2} = parse_additive(Rest, Engine),
{{binary, Op, Left, Right@2}, After@2}
end;
[{keyword, <<"is"/utf8>>},
{keyword, <<"not"/utf8>>},
{keyword, <<"null"/utf8>>} |
Rest@1] ->
{{is_check, Left, is_null, true}, Rest@1};
[{keyword, <<"is"/utf8>>}, {keyword, <<"null"/utf8>>} | Rest@2] ->
{{is_check, Left, is_null, false}, Rest@2};
[{keyword, <<"is"/utf8>>},
{keyword, <<"not"/utf8>>},
{keyword, <<"true"/utf8>>} |
Rest@3] ->
{{is_check, Left, is_true, true}, Rest@3};
[{keyword, <<"is"/utf8>>}, {keyword, <<"true"/utf8>>} | Rest@4] ->
{{is_check, Left, is_true, false}, Rest@4};
[{keyword, <<"is"/utf8>>},
{keyword, <<"not"/utf8>>},
{keyword, <<"false"/utf8>>} |
Rest@5] ->
{{is_check, Left, is_false, true}, Rest@5};
[{keyword, <<"is"/utf8>>}, {keyword, <<"false"/utf8>>} | Rest@6] ->
{{is_check, Left, is_false, false}, Rest@6};
[{keyword, <<"is"/utf8>>},
{keyword, <<"not"/utf8>>},
{keyword, <<"unknown"/utf8>>} |
Rest@7] ->
{{is_check, Left, is_unknown, true}, Rest@7};
[{keyword, <<"is"/utf8>>}, {keyword, <<"unknown"/utf8>>} | Rest@8] ->
{{is_check, Left, is_unknown, false}, Rest@8};
[{keyword, <<"not"/utf8>>}, {keyword, <<"in"/utf8>>} | Rest@9] ->
parse_in_tail(Left, Rest@9, true, Engine);
[{keyword, <<"in"/utf8>>} | Rest@10] ->
parse_in_tail(Left, Rest@10, false, Engine);
[{keyword, <<"not"/utf8>>}, {keyword, <<"between"/utf8>>} | Rest@11] ->
parse_between_tail(Left, Rest@11, true, Engine);
[{keyword, <<"between"/utf8>>} | Rest@12] ->
parse_between_tail(Left, Rest@12, false, Engine);
[{keyword, <<"not"/utf8>>}, {keyword, <<"like"/utf8>>} | Rest@13] ->
parse_like_tail(Left, Rest@13, like, true, Engine);
[{keyword, <<"like"/utf8>>} | Rest@14] ->
parse_like_tail(Left, Rest@14, like, false, Engine);
[{keyword, <<"not"/utf8>>}, {keyword, <<"ilike"/utf8>>} | Rest@15] ->
parse_like_tail(Left, Rest@15, ilike, true, Engine);
[{keyword, <<"ilike"/utf8>>} | Rest@16] ->
parse_like_tail(Left, Rest@16, ilike, false, Engine);
_ ->
{Left, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1320).
?DOC(false).
-spec parse_like_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@query_ir:like_op(),
boolean(),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_like_tail(Left, Tokens, Op, Negated, Engine) ->
{Pattern, Rest} = parse_additive(Tokens, Engine),
{Escape, Rest2} = case Rest of
[{keyword, <<"escape"/utf8>>} | More] ->
{Esc, After} = parse_additive(More, Engine),
{{some, Esc}, After};
_ ->
{none, Rest}
end,
{{like_expr, Left, Op, Pattern, Escape, Negated}, Rest2}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1347).
?DOC(false).
-spec parse_additive(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_additive(Tokens, Engine) ->
{Left, Rest} = parse_multiplicative(Tokens, Engine),
parse_additive_tail(Left, Rest, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1355).
?DOC(false).
-spec parse_additive_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_additive_tail(Left, Tokens, Engine) ->
case Tokens of
[{operator, Op} | Rest] when ((((((Op =:= <<"+"/utf8>>) orelse (Op =:= <<"-"/utf8>>)) orelse (Op =:= <<"||"/utf8>>)) orelse (Op =:= <<"->"/utf8>>)) orelse (Op =:= <<"->>"/utf8>>)) orelse (Op =:= <<"#>"/utf8>>)) orelse (Op =:= <<"#>>"/utf8>>) ->
{Right, After} = parse_multiplicative(Rest, Engine),
parse_additive_tail({binary, Op, Left, Right}, After, Engine);
_ ->
{Left, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1381).
?DOC(false).
-spec parse_multiplicative(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_multiplicative(Tokens, Engine) ->
{Left, Rest} = parse_unary(Tokens, Engine),
parse_multiplicative_tail(Left, Rest, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1389).
?DOC(false).
-spec parse_multiplicative_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_multiplicative_tail(Left, Tokens, Engine) ->
case Tokens of
[star | Rest] ->
{Right, After} = parse_unary(Rest, Engine),
parse_multiplicative_tail(
{binary, <<"*"/utf8>>, Left, Right},
After,
Engine
);
[{operator, Op} | Rest@1] when (Op =:= <<"/"/utf8>>) orelse (Op =:= <<"%"/utf8>>) ->
{Right@1, After@1} = parse_unary(Rest@1, Engine),
parse_multiplicative_tail(
{binary, Op, Left, Right@1},
After@1,
Engine
);
_ ->
{Left, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1415).
?DOC(false).
-spec parse_unary(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_unary(Tokens, Engine) ->
case Tokens of
[{operator, <<"-"/utf8>>} | Rest] ->
{Inner, After} = parse_unary(Rest, Engine),
{{unary, <<"-"/utf8>>, Inner}, After};
[{operator, <<"+"/utf8>>} | Rest@1] ->
{Inner@1, After@1} = parse_unary(Rest@1, Engine),
{{unary, <<"+"/utf8>>, Inner@1}, After@1};
_ ->
parse_cast(Tokens, Engine)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1432).
?DOC(false).
-spec parse_cast(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_cast(Tokens, Engine) ->
{Atom, Rest} = parse_atom(Tokens, Engine),
parse_cast_tail(Atom, Rest).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1501).
?DOC(false).
-spec parse_atom(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_atom(Tokens, Engine) ->
case Tokens of
[] ->
{{raw_expr, <<"empty expression"/utf8>>, []}, []};
[{keyword, <<"null"/utf8>>} | Rest] ->
{null_lit, Rest};
[{keyword, <<"true"/utf8>>} | Rest@1] ->
{{bool_lit, true}, Rest@1};
[{keyword, <<"false"/utf8>>} | Rest@2] ->
{{bool_lit, false}, Rest@2};
[{string_lit, V} | Rest@3] ->
{{string_lit, V}, Rest@3};
[{number_lit, N} | Rest@4] ->
{{number_lit, N}, Rest@4};
[{placeholder, Raw} | Rest@5] ->
Idx = decode_placeholder(Raw),
{{param, Idx, Raw}, Rest@5};
[{keyword, <<"case"/utf8>>} | Rest@6] ->
parse_case(Rest@6, Engine);
[{keyword, <<"cast"/utf8>>}, l_paren | Rest@7] ->
{Inside, After} = collect_parens(Rest@7),
{Expr_tokens, As_rest} = split_on_as(Inside),
Target = begin
_pipe = As_rest,
_pipe@1 = gleam@list:map(_pipe, fun(T) -> case T of
{ident, N@1} ->
N@1;
{keyword, K} ->
K;
_ ->
<<""/utf8>>
end end),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(S) -> S /= <<""/utf8>> end
),
gleam@string:join(_pipe@2, <<" "/utf8>>)
end,
{{cast, parse_expr(Expr_tokens, Engine), Target}, After};
[{keyword, <<"not"/utf8>>},
{keyword, <<"exists"/utf8>>},
l_paren |
Rest@8] ->
{Inside@1, After@1} = collect_parens(Rest@8),
{Core, _} = parse_select_core(Inside@1, Engine),
{{exists, Core, true}, After@1};
[{keyword, <<"exists"/utf8>>}, l_paren | Rest@9] ->
{Inside@2, After@2} = collect_parens(Rest@9),
{Core@1, _} = parse_select_core(Inside@2, Engine),
{{exists, Core@1, false}, After@2};
[{keyword, <<"array"/utf8>>}, l_paren | Rest@10] ->
{Inside@3, After@3} = collect_parens(Rest@10),
{Core@2, _} = parse_select_core(Inside@3, Engine),
{{scalar_subquery, Core@2}, After@3};
[{keyword, <<"array"/utf8>>}, {operator, <<"["/utf8>>} | Rest@11] ->
{Inside@4, After@4} = collect_brackets(Rest@11),
{{array_lit, parse_expr_list(Inside@4, Engine)}, After@4};
[l_paren, {keyword, <<"select"/utf8>>} | _] ->
{Inside@5, After@5} = collect_parens(drop_first(Tokens)),
{Core@3, _} = parse_select_core(Inside@5, Engine),
{{scalar_subquery, Core@3}, After@5};
[l_paren, {keyword, <<"with"/utf8>>} | _] ->
{Inside@6, After@6} = collect_parens(drop_first(Tokens)),
Stmt = parse_stmt(Inside@6, Engine),
case Stmt of
{select_stmt, _, C} ->
{{scalar_subquery, C}, After@6};
_ ->
{{raw_expr,
<<"non-SELECT subquery in expression"/utf8>>,
Tokens},
[]}
end;
[l_paren | Rest@12] ->
{Inside@7, After@7} = collect_parens(Rest@12),
case split_on_top_commas(Inside@7) of
[Single] ->
{parse_expr(Single, Engine), After@7};
Many ->
{{tuple,
gleam@list:map(
Many,
fun(Group) -> parse_expr(Group, Engine) end
)},
After@7}
end;
[{keyword, <<"interval"/utf8>>}, {string_lit, Val} | Rest@13] ->
{{func,
<<"interval"/utf8>>,
[{func_arg, {string_lit, Val}}],
false,
none,
none},
Rest@13};
[{ident, S@1}, dot, {ident, Name}, l_paren | Rest@14] ->
case string:lowercase(S@1) of
<<"sqlode"/utf8>> ->
{Inside@8, After@8} = collect_parens(Rest@14),
{{macro, string:lowercase(Name), Inside@8}, After@8};
_ ->
parse_function_call(Name, [l_paren | Rest@14], Engine)
end;
[{ident, Name@1}, l_paren | Rest@15] ->
parse_function_call(Name@1, Rest@15, Engine);
[{ident, Table}, dot, star | Rest@16] ->
{{star_ref, {some, string:lowercase(Table)}}, Rest@16};
[{ident, Table@1}, dot, {operator, <<"*"/utf8>>} | Rest@17] ->
{{star_ref, {some, string:lowercase(Table@1)}}, Rest@17};
[{ident, Table@2}, dot, {ident, Col} | Rest@18] ->
{{column_ref,
{some, string:lowercase(Table@2)},
string:lowercase(Col)},
Rest@18};
[{ident, Table@3}, dot, {quoted_ident, Col@1} | Rest@19] ->
{{column_ref,
{some, string:lowercase(Table@3)},
string:lowercase(Col@1)},
Rest@19};
[{ident, Name@2} | Rest@20] ->
{{column_ref, none, string:lowercase(Name@2)}, Rest@20};
[{quoted_ident, Name@3} | Rest@21] ->
{{column_ref, none, string:lowercase(Name@3)}, Rest@21};
[star | Rest@22] ->
{{star_ref, none}, Rest@22};
[{operator, <<"*"/utf8>>} | Rest@23] ->
{{star_ref, none}, Rest@23};
_ ->
{{raw_expr, <<"unrecognised atom"/utf8>>, Tokens}, []}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1648).
?DOC(false).
-spec parse_function_call(
binary(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_function_call(Name, Tokens, Engine) ->
{Inside, After} = collect_parens(Tokens),
{Distinct, Args_tokens} = case Inside of
[{keyword, <<"distinct"/utf8>>} | Rest] ->
{true, Rest};
_ ->
{false, Inside}
end,
Args = case Args_tokens of
[] ->
[];
_ ->
_pipe = Args_tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:map(
_pipe@1,
fun(Group) -> {func_arg, parse_expr(Group, Engine)} end
)
end,
{Filter, After_filter} = parse_filter_clause(After, Engine),
{Over, After_over} = parse_over_clause(After_filter, Engine),
{{func, string:lowercase(Name), Args, Distinct, Filter, Over}, After_over}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1702).
?DOC(false).
-spec parse_over_clause(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {gleam@option:option(sqlode@internal@query_ir:window_spec()),
list(sqlode@internal@lexer:token())}.
parse_over_clause(Tokens, Engine) ->
case Tokens of
[{keyword, <<"over"/utf8>>}, l_paren | Rest] ->
{Inside, After} = collect_parens(Rest),
{{some, parse_window_spec(Inside, Engine)}, After};
[{keyword, <<"over"/utf8>>}, {ident, _} | Rest@1] ->
{{some, {window_spec, [], [], none}}, Rest@1};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 394).
?DOC(false).
-spec parse_select_item(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:select_item_ex().
parse_select_item(Tokens, Engine) ->
case Tokens of
[{operator, <<"*"/utf8>>}] ->
{star_ex, none};
[star] ->
{star_ex, none};
[{ident, T}, dot, {operator, <<"*"/utf8>>}] ->
{star_ex, {some, string:lowercase(T)}};
[{ident, T@1}, dot, star] ->
{star_ex, {some, string:lowercase(T@1)}};
_ ->
{Expr_tokens, Alias} = split_trailing_alias(Tokens),
{expr_item, parse_expr(Expr_tokens, Engine), Alias}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 385).
?DOC(false).
-spec parse_select_items(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(sqlode@internal@query_ir:select_item_ex()).
parse_select_items(Tokens, Engine) ->
_pipe = Tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:map(_pipe@1, fun(Group) -> parse_select_item(Group, Engine) end).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 548).
?DOC(false).
-spec collect_values_rows(
list(sqlode@internal@lexer:token()),
list(list(sqlode@internal@query_ir:expr())),
sqlode@internal@model:engine()
) -> list(list(sqlode@internal@query_ir:expr())).
collect_values_rows(Tokens, Acc, Engine) ->
case Tokens of
[l_paren | Rest] ->
{Inner, After} = collect_parens(Rest),
Row = gleam@list:map(
split_on_top_commas(Inner),
fun(Group) -> parse_expr(Group, Engine) end
),
case After of
[comma | More] ->
collect_values_rows(More, [Row | Acc], Engine);
_ ->
lists:reverse([Row | Acc])
end;
_ ->
lists:reverse(Acc)
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 538).
?DOC(false).
-spec parse_values_rows(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(list(sqlode@internal@query_ir:expr())).
parse_values_rows(Body, Engine) ->
case Body of
[{keyword, <<"values"/utf8>>} | Rest] ->
collect_values_rows(Rest, [], Engine);
_ ->
[]
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 524).
?DOC(false).
-spec parse_values_from_body(
list(sqlode@internal@lexer:token()),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:from_item_ex(),
list(sqlode@internal@lexer:token())}.
parse_values_from_body(Body, After, Engine) ->
Rows = parse_values_rows(Body, Engine),
{Alias_opt, Col_aliases, Rest} = parse_aliased_column_list(After),
Alias = gleam@option:unwrap(Alias_opt, <<""/utf8>>),
{{from_values, Rows, Alias, Col_aliases}, Rest}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 479).
?DOC(false).
-spec parse_primary_from(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:from_item_ex(),
list(sqlode@internal@lexer:token())}.
parse_primary_from(Tokens, Engine) ->
case Tokens of
[l_paren, {keyword, <<"values"/utf8>>} | Rest] ->
{_, After} = collect_parens([{keyword, <<"values"/utf8>>} | Rest]),
{Body, _} = collect_parens([{keyword, <<"values"/utf8>>} | Rest]),
parse_values_from_body(Body, After, Engine);
[l_paren, {keyword, <<"select"/utf8>>} | _] ->
{Inner, After_rp} = collect_parens(drop_first(Tokens)),
{Core, _} = parse_select_core(Inner, Engine),
parse_subquery_alias(Core, After_rp, false);
[l_paren, {keyword, <<"with"/utf8>>} | _] ->
{Inner@1, After_rp@1} = collect_parens(drop_first(Tokens)),
Stmt = parse_stmt(Inner@1, Engine),
Core@1 = case Stmt of
{select_stmt, _, C} ->
C;
_ ->
empty_core()
end,
parse_subquery_alias(Core@1, After_rp@1, false);
[{keyword, <<"lateral"/utf8>>},
l_paren,
{keyword, <<"select"/utf8>>} |
_] ->
{Inner@2, After_rp@2} = collect_parens(
drop_first(drop_first(Tokens))
),
{Core@2, _} = parse_select_core(Inner@2, Engine),
parse_subquery_alias(Core@2, After_rp@2, true);
[{ident, _}, dot, {ident, Name} | Rest@1] ->
{Alias, Rest2} = parse_optional_alias(Rest@1),
{{from_table, string:lowercase(Name), Alias}, Rest2};
[{ident, Name@1} | Rest@2] ->
{Alias@1, Rest2@1} = parse_optional_alias(Rest@2),
{{from_table, string:lowercase(Name@1), Alias@1}, Rest2@1};
[{quoted_ident, Name@2} | Rest@3] ->
{Alias@2, Rest2@2} = parse_optional_alias(Rest@3),
{{from_table, string:lowercase(Name@2), Alias@2}, Rest2@2};
_ ->
{{from_table, <<""/utf8>>, none}, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 731).
?DOC(false).
-spec parse_join_on(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:join_on(), list(sqlode@internal@lexer:token())}.
parse_join_on(Tokens, Engine) ->
case Tokens of
[{keyword, <<"on"/utf8>>} | Rest] ->
{On_toks, After} = collect_until_keyword(
Rest,
[<<"join"/utf8>>,
<<"inner"/utf8>>,
<<"left"/utf8>>,
<<"right"/utf8>>,
<<"full"/utf8>>,
<<"cross"/utf8>>,
<<"natural"/utf8>>,
<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"returning"/utf8>>,
<<"window"/utf8>>]
),
{{join_on_expr, parse_expr(On_toks, Engine)}, After};
[{keyword, <<"using"/utf8>>}, l_paren | Rest@1] ->
{Cols, After@1} = collect_parens(Rest@1),
{{join_using, parse_ident_list(Cols)}, After@1};
_ ->
{join_no_condition, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 653).
?DOC(false).
-spec parse_join_tail(
sqlode@internal@query_ir:from_item_ex(),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:from_item_ex().
parse_join_tail(Left, Tokens, Engine) ->
case detect_join(Tokens) of
{some, {Kind, After_kw, Lateral}} ->
{Right_raw, After_right} = parse_primary_from(After_kw, Engine),
{On, After_on} = parse_join_on(After_right, Engine),
Right = parse_join_tail(Right_raw, After_on, Engine),
parse_join_tail(
{from_join, Left, Right, Kind, On, Lateral},
[],
Engine
);
none ->
Left
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 471).
?DOC(false).
-spec parse_from_element(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:from_item_ex().
parse_from_element(Tokens, Engine) ->
{First, After_first} = parse_primary_from(Tokens, Engine),
parse_join_tail(First, After_first, Engine).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 456).
?DOC(false).
-spec parse_from_clause(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {list(sqlode@internal@query_ir:from_item_ex()),
list(sqlode@internal@lexer:token())}.
parse_from_clause(Tokens, Engine) ->
{Items_tokens, Rest} = collect_until_keyword(
Tokens,
[<<"where"/utf8>>,
<<"group"/utf8>>,
<<"having"/utf8>>,
<<"order"/utf8>>,
<<"limit"/utf8>>,
<<"offset"/utf8>>,
<<"union"/utf8>>,
<<"intersect"/utf8>>,
<<"except"/utf8>>,
<<"window"/utf8>>,
<<"returning"/utf8>>]
),
Groups = split_on_top_commas(Items_tokens),
From_items = gleam@list:map(
Groups,
fun(Group) -> parse_from_element(Group, Engine) end
),
{From_items, Rest}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 835).
?DOC(false).
-spec parse_insert_source(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:insert_source(),
list(sqlode@internal@lexer:token())}.
parse_insert_source(Tokens, Engine) ->
case Tokens of
[{keyword, <<"default"/utf8>>}, {keyword, <<"values"/utf8>>} | Rest] ->
{insert_default_values, Rest};
[{keyword, <<"values"/utf8>>} | Rest@1] ->
Rows = collect_values_rows(Rest@1, [], Engine),
After = skip_values_rows(Rest@1),
{{insert_values, Rows}, After};
[{keyword, <<"select"/utf8>>} | _] ->
{Core, After@1} = parse_select_core(Tokens, Engine),
{{insert_select, Core}, After@1};
[{keyword, <<"with"/utf8>>} | _] ->
Stmt = parse_stmt(Tokens, Engine),
case Stmt of
{select_stmt, _, C} ->
{{insert_select, C}, []};
_ ->
{{insert_values, []}, Tokens}
end;
_ ->
{{insert_values, []}, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 938).
?DOC(false).
-spec parse_assignment(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {ok, sqlode@internal@query_ir:assignment()} | {error, nil}.
parse_assignment(Tokens, Engine) ->
case Tokens of
[{ident, Col}, {operator, <<"="/utf8>>} | Rest] ->
{ok, {assignment, string:lowercase(Col), parse_expr(Rest, Engine)}};
[{quoted_ident, Col@1}, {operator, <<"="/utf8>>} | Rest@1] ->
{ok,
{assignment,
string:lowercase(Col@1),
parse_expr(Rest@1, Engine)}};
_ ->
{error, nil}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 929).
?DOC(false).
-spec parse_assignments(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(sqlode@internal@query_ir:assignment()).
parse_assignments(Tokens, Engine) ->
_pipe = Tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:filter_map(
_pipe@1,
fun(Group) -> parse_assignment(Group, Engine) end
).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 806).
?DOC(false).
-spec parse_on_duplicate_key_update(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {list(sqlode@internal@query_ir:assignment()),
list(sqlode@internal@lexer:token())}.
parse_on_duplicate_key_update(Tokens, Engine) ->
case Tokens of
[{keyword, <<"on"/utf8>>},
{ident, Duplicate},
{keyword, <<"key"/utf8>>},
{keyword, <<"update"/utf8>>} |
Rest] ->
case string:lowercase(Duplicate) of
<<"duplicate"/utf8>> ->
{Assign_toks, After} = collect_until_keyword(
Rest,
[<<"returning"/utf8>>]
),
{parse_assignments(Assign_toks, Engine), After};
_ ->
{[], Tokens}
end;
_ ->
{[], Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 995).
?DOC(false).
-spec parse_returning(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(sqlode@internal@query_ir:select_item_ex()).
parse_returning(Tokens, Engine) ->
case skip_to_returning_or_end(Tokens) of
[{keyword, <<"returning"/utf8>>} | Rest] ->
Filtered = gleam@list:filter(Rest, fun(T) -> case T of
semicolon ->
false;
_ ->
true
end end),
parse_select_items(Filtered, Engine);
_ ->
[]
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 773).
?DOC(false).
-spec parse_insert_target(
list(sqlode@internal@query_ir:cte_def()),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:stmt().
parse_insert_target(Ctes, Tokens, Engine) ->
{Table_name, After_name} = read_qualified_name(Tokens),
{Columns, After_cols} = case After_name of
[l_paren | After_lp] ->
{Inside, After} = collect_parens(After_lp),
{parse_ident_list(Inside), After};
_ ->
{[], After_name}
end,
{Source, After_source} = parse_insert_source(After_cols, Engine),
{On_duplicate, After_on_dup} = case Engine of
my_s_q_l ->
parse_on_duplicate_key_update(After_source, Engine);
_ ->
{[], After_source}
end,
Returning = parse_returning(After_on_dup, Engine),
{insert_stmt, Ctes, Table_name, Columns, Source, On_duplicate, Returning}.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 757).
?DOC(false).
-spec parse_insert_body(
list(sqlode@internal@query_ir:cte_def()),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:stmt().
parse_insert_body(Ctes, Tokens, Engine) ->
case sqlode@internal@query_analyzer@token_utils:strip_insert_or_action(
Tokens
) of
[{keyword, <<"into"/utf8>>} | After_into] ->
parse_insert_target(Ctes, After_into, Engine);
_ ->
{unstructured_stmt,
<<"INSERT without INTO"/utf8>>,
[{keyword, <<"insert"/utf8>>} | Tokens]}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 887).
?DOC(false).
-spec parse_update_body(
list(sqlode@internal@query_ir:cte_def()),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:stmt().
parse_update_body(Ctes, Tokens, Engine) ->
{Table_name, After_name} = read_qualified_name(Tokens),
{Alias, After_alias} = parse_optional_alias(After_name),
case After_alias of
[{keyword, <<"set"/utf8>>} | After_set] ->
{Set_toks, After_set_block} = collect_until_keyword(
After_set,
[<<"from"/utf8>>, <<"where"/utf8>>, <<"returning"/utf8>>]
),
Assignments = parse_assignments(Set_toks, Engine),
{From_list, After_from} = case After_set_block of
[{keyword, <<"from"/utf8>>} | Rest] ->
parse_from_clause(Rest, Engine);
_ ->
{[], After_set_block}
end,
{Where_expr, After_where} = case After_from of
[{keyword, <<"where"/utf8>>} | Rest@1] ->
{Where_toks, After} = collect_until_keyword(
Rest@1,
[<<"returning"/utf8>>]
),
{{some, parse_expr(Where_toks, Engine)}, After};
_ ->
{none, After_from}
end,
Returning = parse_returning(After_where, Engine),
{update_stmt,
Ctes,
Table_name,
Alias,
Assignments,
From_list,
Where_expr,
Returning};
_ ->
{unstructured_stmt,
<<"UPDATE without SET"/utf8>>,
[{keyword, <<"update"/utf8>>} | Tokens]}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 957).
?DOC(false).
-spec parse_delete_body(
list(sqlode@internal@query_ir:cte_def()),
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> sqlode@internal@query_ir:stmt().
parse_delete_body(Ctes, Tokens, Engine) ->
case Tokens of
[{keyword, <<"from"/utf8>>} | After_from] ->
{Table_name, After_name} = read_qualified_name(After_from),
{Alias, After_alias} = parse_optional_alias(After_name),
{Using_list, After_using} = case After_alias of
[{keyword, <<"using"/utf8>>} | Rest] ->
parse_from_clause(Rest, Engine);
_ ->
{[], After_alias}
end,
{Where_expr, After_where} = case After_using of
[{keyword, <<"where"/utf8>>} | Rest@1] ->
{Where_toks, After} = collect_until_keyword(
Rest@1,
[<<"returning"/utf8>>]
),
{{some, parse_expr(Where_toks, Engine)}, After};
_ ->
{none, After_using}
end,
Returning = parse_returning(After_where, Engine),
{delete_stmt,
Ctes,
Table_name,
Alias,
Using_list,
Where_expr,
Returning};
_ ->
{unstructured_stmt,
<<"DELETE without FROM"/utf8>>,
[{keyword, <<"delete"/utf8>>} | Tokens]}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1038).
?DOC(false).
-spec parse_expr_list(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> list(sqlode@internal@query_ir:expr()).
parse_expr_list(Tokens, Engine) ->
_pipe = Tokens,
_pipe@1 = split_on_top_commas(_pipe),
gleam@list:map(_pipe@1, fun(Group) -> parse_expr(Group, Engine) end).
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1233).
?DOC(false).
-spec parse_in_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
boolean(),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_in_tail(Left, Tokens, Negated, Engine) ->
case Tokens of
[l_paren, {keyword, <<"select"/utf8>>} | _] ->
{Inner, After} = collect_parens(drop_first(Tokens)),
{Core, _} = parse_select_core(Inner, Engine),
{{in_expr, Left, {in_subquery, Core}, Negated}, After};
[l_paren | Rest] ->
{Inner@1, After@1} = collect_parens(Rest),
case detect_slice_macro(Inner@1) of
{some, Name} ->
{{in_expr, Left, {in_slice_macro, Name}, Negated}, After@1};
none ->
Values = parse_expr_list(Inner@1, Engine),
{{in_expr, Left, {in_list, Values}, Negated}, After@1}
end;
_ ->
{{raw_expr, <<"IN without paren list"/utf8>>, Tokens}, []}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1301).
?DOC(false).
-spec parse_between_tail(
sqlode@internal@query_ir:expr(),
list(sqlode@internal@lexer:token()),
boolean(),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_between_tail(Left, Tokens, Negated, Engine) ->
{Low, Rest} = parse_additive(Tokens, Engine),
case Rest of
[{keyword, <<"and"/utf8>>} | After_and] ->
{High, After} = parse_additive(After_and, Engine),
{{between, Left, Low, High, Negated}, After};
_ ->
{{raw_expr, <<"BETWEEN without AND"/utf8>>, Tokens}, []}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1681).
?DOC(false).
-spec parse_filter_clause(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {gleam@option:option(sqlode@internal@query_ir:expr()),
list(sqlode@internal@lexer:token())}.
parse_filter_clause(Tokens, Engine) ->
case Tokens of
[{keyword, <<"filter"/utf8>>},
l_paren,
{keyword, <<"where"/utf8>>} |
Rest] ->
{Inside, After} = collect_parens_after_where(Rest),
{{some, parse_expr(Inside, Engine)}, After};
_ ->
{none, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1776).
?DOC(false).
-spec collect_case_branches(
list(sqlode@internal@lexer:token()),
gleam@option:option(sqlode@internal@query_ir:expr()),
list(sqlode@internal@query_ir:case_branch()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
collect_case_branches(Tokens, Scrutinee, Branches, Engine) ->
case Tokens of
[{keyword, <<"when"/utf8>>} | Rest] ->
{When_toks, After_when} = collect_until_keyword(
Rest,
[<<"then"/utf8>>]
),
After_then = case After_when of
[{keyword, <<"then"/utf8>>} | More] ->
More;
_ ->
After_when
end,
{Then_toks, After_then_block} = collect_until_keyword(
After_then,
[<<"when"/utf8>>, <<"else"/utf8>>, <<"end"/utf8>>]
),
Branch = {case_branch,
parse_expr(When_toks, Engine),
parse_expr(Then_toks, Engine)},
collect_case_branches(
After_then_block,
Scrutinee,
[Branch | Branches],
Engine
);
[{keyword, <<"else"/utf8>>} | Rest@1] ->
{Else_toks, After_else} = collect_until_keyword(
Rest@1,
[<<"end"/utf8>>]
),
After_end = case After_else of
[{keyword, <<"end"/utf8>>} | More@1] ->
More@1;
_ ->
After_else
end,
{{'case',
Scrutinee,
lists:reverse(Branches),
{some, parse_expr(Else_toks, Engine)}},
After_end};
[{keyword, <<"end"/utf8>>} | Rest@2] ->
{{'case', Scrutinee, lists:reverse(Branches), none}, Rest@2};
_ ->
{{'case', Scrutinee, lists:reverse(Branches), none}, Tokens}
end.
-file("src/sqlode/internal/query_analyzer/expr_parser.gleam", 1758).
?DOC(false).
-spec parse_case(
list(sqlode@internal@lexer:token()),
sqlode@internal@model:engine()
) -> {sqlode@internal@query_ir:expr(), list(sqlode@internal@lexer:token())}.
parse_case(Tokens, Engine) ->
{Scrutinee, Rest} = case Tokens of
[{keyword, <<"when"/utf8>>} | _] ->
{none, Tokens};
_ ->
{Scr_toks, After} = collect_until_keyword(
Tokens,
[<<"when"/utf8>>, <<"else"/utf8>>, <<"end"/utf8>>]
),
case Scr_toks of
[] ->
{none, After};
_ ->
{{some, parse_expr(Scr_toks, Engine)}, After}
end
end,
collect_case_branches(Rest, Scrutinee, [], Engine).