Current section

Files

Jump to
sqlode src sqlode@internal@query_validation.erl
Raw

src/sqlode@internal@query_validation.erl

-module(sqlode@internal@query_validation).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/sqlode/internal/query_validation.gleam").
-export([validate_no_duplicate_names/1, validate_unsupported_annotations/1, validate_array_engine_support/2, validate_native_annotations/1, error_to_string/1]).
-export_type([validation_error/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 validation_error() :: {duplicate_name, binary(), list(binary())} |
{normalized_name_collision, binary(), list(binary()), list(binary())} |
{unsupported_annotation, binary(), binary(), binary()} |
{unsupported_array_for_engine, binary(), binary()} |
{unsupported_slice_for_engine, binary(), binary()}.
-file("src/sqlode/internal/query_validation.gleam", 65).
?DOC(false).
-spec find_duplicate_group(
list(sqlode@internal@query_ir:tokenized_query()),
fun((sqlode@internal@query_ir:tokenized_query()) -> binary())
) -> {ok, {binary(), list(sqlode@internal@query_ir:tokenized_query())}} |
{error, nil}.
find_duplicate_group(Queries, Key) ->
_pipe = gleam@list:group(Queries, Key),
_pipe@1 = maps:to_list(_pipe),
gleam@list:find(
_pipe@1,
fun(Entry) -> erlang:length(erlang:element(2, Entry)) > 1 end
).
-file("src/sqlode/internal/query_validation.gleam", 40).
?DOC(false).
-spec validate_no_duplicate_names(
list(sqlode@internal@query_ir:tokenized_query())
) -> {ok, nil} | {error, validation_error()}.
validate_no_duplicate_names(Queries) ->
case find_duplicate_group(
Queries,
fun(Q) -> erlang:element(2, erlang:element(2, Q)) end
) of
{ok, {Name, Dupes}} ->
Paths = begin
_pipe = Dupes,
_pipe@1 = gleam@list:map(
_pipe,
fun(Q@1) -> erlang:element(6, erlang:element(2, Q@1)) end
),
gleam@list:unique(_pipe@1)
end,
{error, {duplicate_name, Name, Paths}};
{error, nil} ->
case find_duplicate_group(
Queries,
fun(Q@2) -> erlang:element(3, erlang:element(2, Q@2)) end
) of
{ok, {Function_name, Colliding}} ->
Names = begin
_pipe@2 = Colliding,
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Q@3) ->
erlang:element(2, erlang:element(2, Q@3))
end
),
gleam@list:unique(_pipe@3)
end,
Paths@1 = begin
_pipe@4 = Colliding,
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Q@4) ->
erlang:element(6, erlang:element(2, Q@4))
end
),
gleam@list:unique(_pipe@5)
end,
{error,
{normalized_name_collision,
Function_name,
Names,
Paths@1}};
{error, nil} ->
{ok, nil}
end
end.
-file("src/sqlode/internal/query_validation.gleam", 78).
?DOC(false).
-spec validate_unsupported_annotations(
list(sqlode@internal@model:analyzed_query())
) -> {ok, nil} | {error, validation_error()}.
validate_unsupported_annotations(Queries) ->
Unsupported = fun(Command) -> case Command of
query_batch_one ->
true;
query_batch_many ->
true;
query_batch_exec ->
true;
query_copy_from ->
true;
_ ->
false
end end,
case gleam@list:find(
Queries,
fun(Q) -> Unsupported(erlang:element(4, erlang:element(2, Q))) end
) of
{ok, Q@1} ->
{Command@1, Alternative} = case erlang:element(
4,
erlang:element(2, Q@1)
) of
query_batch_one ->
{<<":batchone"/utf8>>, <<":one"/utf8>>};
query_batch_many ->
{<<":batchmany"/utf8>>, <<":many"/utf8>>};
query_batch_exec ->
{<<":batchexec"/utf8>>, <<":exec"/utf8>>};
query_copy_from ->
{<<":copyfrom"/utf8>>, <<":exec"/utf8>>};
_ ->
{<<""/utf8>>, <<":exec"/utf8>>}
end,
{error,
{unsupported_annotation,
erlang:element(2, erlang:element(2, Q@1)),
Command@1,
<<<<<<Command@1/binary, " is not yet supported. Use "/utf8>>/binary,
Alternative/binary>>/binary,
" instead, or add '-- sqlode:skip' before the annotation to bypass this query"/utf8>>}};
{error, _} ->
{ok, nil}
end.
-file("src/sqlode/internal/query_validation.gleam", 112).
?DOC(false).
-spec is_slice_macro(sqlode@internal@model:macro()) -> boolean().
is_slice_macro(Item) ->
case Item of
{macro_slice, _, _} ->
true;
_ ->
false
end.
-file("src/sqlode/internal/query_validation.gleam", 131).
?DOC(false).
-spec validate_array_engine_support(
sqlode@internal@model:engine(),
list(sqlode@internal@model:analyzed_query())
) -> {ok, nil} | {error, validation_error()}.
validate_array_engine_support(Engine, Queries) ->
case Engine of
postgre_s_q_l ->
{ok, nil};
_ ->
Engine_name = sqlode@internal@model:engine_to_string(Engine),
Has_array_param = fun(Q) ->
gleam@list:any(
erlang:element(3, Q),
fun(P) -> case erlang:element(4, P) of
{array_type, _} ->
true;
_ ->
false
end end
)
end,
Has_slice_macro = fun(Q@1) ->
gleam@list:any(
erlang:element(8, erlang:element(2, Q@1)),
fun is_slice_macro/1
)
end,
case gleam@list:find(Queries, Has_array_param) of
{ok, Q@2} ->
{error,
{unsupported_array_for_engine,
erlang:element(2, erlang:element(2, Q@2)),
Engine_name}};
{error, _} ->
case gleam@list:find(Queries, Has_slice_macro) of
{ok, Q@3} ->
{error,
{unsupported_slice_for_engine,
erlang:element(2, erlang:element(2, Q@3)),
Engine_name}};
{error, _} ->
{ok, nil}
end
end
end.
-file("src/sqlode/internal/query_validation.gleam", 174).
?DOC(false).
-spec validate_native_annotations(list(sqlode@internal@model:analyzed_query())) -> {ok,
nil} |
{error, validation_error()}.
validate_native_annotations(Queries) ->
case gleam@list:find(
Queries,
fun(Q) ->
erlang:element(4, erlang:element(2, Q)) =:= query_exec_result
end
) of
{ok, Q@1} ->
{error,
{unsupported_annotation,
erlang:element(2, erlang:element(2, Q@1)),
<<":execresult"/utf8>>,
<<":execresult is not supported with native runtime. Use :exec, :execrows, or :execlastid instead"/utf8>>}};
{error, _} ->
{ok, nil}
end.
-file("src/sqlode/internal/query_validation.gleam", 222).
?DOC(false).
-spec quote_join(list(binary())) -> binary().
quote_join(Names) ->
_pipe = Names,
_pipe@1 = gleam@list:map(
_pipe,
fun(N) -> <<<<"\""/utf8, N/binary>>/binary, "\""/utf8>> end
),
gleam@string:join(_pipe@1, <<", "/utf8>>).
-file("src/sqlode/internal/query_validation.gleam", 188).
?DOC(false).
-spec error_to_string(validation_error()) -> binary().
error_to_string(Error) ->
case Error of
{duplicate_name, Name, Paths} ->
<<<<<<"duplicate query name \""/utf8, Name/binary>>/binary,
"\" found in: "/utf8>>/binary,
(gleam@string:join(Paths, <<", "/utf8>>))/binary>>;
{normalized_name_collision, Function_name, Names, Paths@1} ->
<<<<<<<<<<<<"query names "/utf8, (quote_join(Names))/binary>>/binary,
" all normalize to the generated identifier \""/utf8>>/binary,
Function_name/binary>>/binary,
"\" (found in: "/utf8>>/binary,
(gleam@string:join(Paths@1, <<", "/utf8>>))/binary>>/binary,
"). Rename one of them so the derived function, params, row, and decoder identifiers are unique."/utf8>>;
{unsupported_annotation, Query_name, Command, Detail} ->
<<<<<<<<<<"Query "/utf8, Query_name/binary>>/binary, " uses "/utf8>>/binary,
Command/binary>>/binary,
": "/utf8>>/binary,
Detail/binary>>;
{unsupported_array_for_engine, Query_name@1, Engine} ->
<<<<<<<<"Query \""/utf8, Query_name@1/binary>>/binary,
"\": array parameters are not supported for engine \""/utf8>>/binary,
Engine/binary>>/binary,
"\". Arrays are only supported with PostgreSQL"/utf8>>;
{unsupported_slice_for_engine, Query_name@2, Engine@1} ->
<<<<<<<<<<<<"Query \""/utf8, Query_name@2/binary>>/binary,
"\": sqlode.slice() is not supported for engine \""/utf8>>/binary,
Engine@1/binary>>/binary,
"\". The native "/utf8>>/binary,
Engine@1/binary>>/binary,
" adapter cannot bind array values at runtime; either switch the block to PostgreSQL, drop the slice macro and inline the IN-clause placeholders, or move the query to runtime: raw and bind the expanded list yourself"/utf8>>
end.