Skip to main content

src/gdo@statement.erl

-module(gdo@statement).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gdo/statement.gleam").
-export([prepare/1, sql/1, placeholder_style/1, bind/3, uses_parameters/1, validate_params/2, exec/2, execute/2, query_all/2, query_one/2, query_all_as/3, query_one_as/3]).
-export_type([placeholder_style/0, statement_backend/0, scan_state/0, placeholder_summary/0, statement/0]).

-type placeholder_style() :: no_parameters |
    positional_parameters |
    named_parameters.

-type statement_backend() :: unbound |
    {bound, gdo@driver:driver_contract(), gdo@driver:driver_statement_state()}.

-type scan_state() :: normal |
    in_single_quoted_string |
    in_double_quoted_string |
    in_line_comment |
    in_block_comment.

-type placeholder_summary() :: {placeholder_summary, boolean(), boolean()}.

-opaque statement() :: {statement,
        binary(),
        placeholder_style(),
        statement_backend()}.

-file("src/gdo/statement.gleam", 295).
-spec set_has_named(placeholder_summary()) -> placeholder_summary().
set_has_named(Summary) ->
    {placeholder_summary, Has_positional, _} = Summary,
    {placeholder_summary, Has_positional, true}.

-file("src/gdo/statement.gleam", 307).
-spec is_ascii_letter(integer()) -> boolean().
is_ascii_letter(Codepoint) ->
    ((Codepoint >= 65) andalso (Codepoint =< 90)) orelse ((Codepoint >= 97)
    andalso (Codepoint =< 122)).

-file("src/gdo/statement.gleam", 312).
-spec ascii_codepoint(binary()) -> {ok, integer()} | {error, nil}.
ascii_codepoint(Grapheme) ->
    case gleam@string:to_utf_codepoints(Grapheme) of
        [Codepoint] ->
            {ok, gleam_stdlib:identity(Codepoint)};

        _ ->
            {error, nil}
    end.

-file("src/gdo/statement.gleam", 300).
-spec is_identifier_start(binary()) -> boolean().
is_identifier_start(Grapheme) ->
    case ascii_codepoint(Grapheme) of
        {ok, Codepoint} ->
            is_ascii_letter(Codepoint) orelse (Codepoint =:= 95);

        {error, _} ->
            false
    end.

-file("src/gdo/statement.gleam", 290).
-spec set_has_positional(placeholder_summary()) -> placeholder_summary().
set_has_positional(Summary) ->
    {placeholder_summary, _, Has_named} = Summary,
    {placeholder_summary, true, Has_named}.

-file("src/gdo/statement.gleam", 237).
-spec scan_placeholders(list(binary()), scan_state(), placeholder_summary()) -> placeholder_summary().
scan_placeholders(Graphemes, State, Summary) ->
    case Graphemes of
        [] ->
            Summary;

        [<<"'"/utf8>>, <<"'"/utf8>> | Rest] when State =:= in_single_quoted_string ->
            scan_placeholders(Rest, in_single_quoted_string, Summary);

        [<<"\""/utf8>>, <<"\""/utf8>> | Rest@1] when State =:= in_double_quoted_string ->
            scan_placeholders(Rest@1, in_double_quoted_string, Summary);

        [<<"'"/utf8>> | Rest@2] ->
            case State of
                normal ->
                    scan_placeholders(Rest@2, in_single_quoted_string, Summary);

                in_single_quoted_string ->
                    scan_placeholders(Rest@2, normal, Summary);

                _ ->
                    scan_placeholders(Rest@2, State, Summary)
            end;

        [<<"\""/utf8>> | Rest@3] ->
            case State of
                normal ->
                    scan_placeholders(Rest@3, in_double_quoted_string, Summary);

                in_double_quoted_string ->
                    scan_placeholders(Rest@3, normal, Summary);

                _ ->
                    scan_placeholders(Rest@3, State, Summary)
            end;

        [<<"-"/utf8>>, <<"-"/utf8>> | Rest@4] when State =:= normal ->
            scan_placeholders(Rest@4, in_line_comment, Summary);

        [<<"/"/utf8>>, <<"*"/utf8>> | Rest@5] when State =:= normal ->
            scan_placeholders(Rest@5, in_block_comment, Summary);

        [<<"*"/utf8>>, <<"/"/utf8>> | Rest@6] when State =:= in_block_comment ->
            scan_placeholders(Rest@6, normal, Summary);

        [<<"\n"/utf8>> | Rest@7] when State =:= in_line_comment ->
            scan_placeholders(Rest@7, normal, Summary);

        [<<"?"/utf8>> | Rest@8] when State =:= normal ->
            scan_placeholders(Rest@8, normal, set_has_positional(Summary));

        [<<":"/utf8>>, Next | Rest@9] ->
            case {State, is_identifier_start(Next)} of
                {normal, true} ->
                    scan_placeholders(Rest@9, normal, set_has_named(Summary));

                {_, _} ->
                    scan_placeholders([Next | Rest@9], State, Summary)
            end;

        [_ | Rest@10] ->
            scan_placeholders(Rest@10, State, Summary)
    end.

-file("src/gdo/statement.gleam", 220).
-spec infer_placeholder_style(binary()) -> {ok, placeholder_style()} |
    {error, gdo@error:error()}.
infer_placeholder_style(Sql) ->
    {placeholder_summary, Has_positional, Has_named} = begin
        _pipe = Sql,
        _pipe@1 = gleam@string:to_graphemes(_pipe),
        scan_placeholders(_pipe@1, normal, {placeholder_summary, false, false})
    end,
    case {Has_positional, Has_named} of
        {true, true} ->
            {error,
                {invalid_configuration,
                    <<"Cannot mix positional and named parameters in the same statement."/utf8>>}};

        {true, false} ->
            {ok, positional_parameters};

        {false, true} ->
            {ok, named_parameters};

        {false, false} ->
            {ok, no_parameters}
    end.

-file("src/gdo/statement.gleam", 45).
-spec prepare(binary()) -> {ok, statement()} | {error, gdo@error:error()}.
prepare(Sql) ->
    Trimmed_sql = gleam@string:trim(Sql),
    case gleam@string:is_empty(Trimmed_sql) of
        true ->
            {error, {invalid_configuration, <<"SQL cannot be empty."/utf8>>}};

        false ->
            case infer_placeholder_style(Trimmed_sql) of
                {ok, Placeholder_style} ->
                    {ok, {statement, Trimmed_sql, Placeholder_style, unbound}};

                {error, Error} ->
                    {error, Error}
            end
    end.

-file("src/gdo/statement.gleam", 59).
-spec sql(statement()) -> binary().
sql(Statement) ->
    {statement, Sql, _, _} = Statement,
    Sql.

-file("src/gdo/statement.gleam", 64).
-spec placeholder_style(statement()) -> placeholder_style().
placeholder_style(Statement) ->
    {statement, _, Placeholder_style, _} = Statement,
    Placeholder_style.

-file("src/gdo/statement.gleam", 69).
-spec bind(
    statement(),
    gdo@driver:driver_contract(),
    gdo@driver:driver_statement_state()
) -> statement().
bind(Statement, Contract, Statement_state) ->
    {statement, Sql, Placeholder_style, _} = Statement,
    {statement, Sql, Placeholder_style, {bound, Contract, Statement_state}}.

-file("src/gdo/statement.gleam", 82).
-spec uses_parameters(statement()) -> boolean().
uses_parameters(Statement) ->
    case placeholder_style(Statement) of
        no_parameters ->
            false;

        _ ->
            true
    end.

-file("src/gdo/statement.gleam", 89).
-spec validate_params(statement(), list(gdo@value:param())) -> {ok, nil} |
    {error, gdo@error:error()}.
validate_params(Statement, Params) ->
    case placeholder_style(Statement) of
        no_parameters ->
            case Params of
                [] ->
                    {ok, nil};

                _ ->
                    {error,
                        {invalid_configuration,
                            <<"This statement does not accept parameters."/utf8>>}}
            end;

        positional_parameters ->
            case gleam@list:any(Params, fun(Param) -> case Param of
                        {named, _, _} ->
                            true;

                        {positional, _} ->
                            false
                    end end) of
                true ->
                    {error,
                        {invalid_configuration,
                            <<"Positional statements require positional parameters only."/utf8>>}};

                false ->
                    {ok, nil}
            end;

        named_parameters ->
            case gleam@list:any(Params, fun(Param@1) -> case Param@1 of
                        {positional, _} ->
                            true;

                        {named, _, _} ->
                            false
                    end end) of
                true ->
                    {error,
                        {invalid_configuration,
                            <<"Named statements require named parameters only."/utf8>>}};

                false ->
                    {ok, nil}
            end
    end.

-file("src/gdo/statement.gleam", 215).
-spec backend(statement()) -> statement_backend().
backend(Statement) ->
    {statement, _, _, Backend} = Statement,
    Backend.

-file("src/gdo/statement.gleam", 137).
-spec exec(statement(), list(gdo@value:param())) -> {ok,
        gdo@result:execution_result()} |
    {error, gdo@error:error()}.
exec(Statement, Params) ->
    case validate_params(Statement, Params) of
        {ok, _} ->
            case backend(Statement) of
                {bound, Contract, Statement_state} ->
                    gdo@driver:exec(Contract, Statement_state, Params);

                unbound ->
                    {ok, gdo@result:execution_result(0, none)}
            end;

        {error, Error} ->
            {error, Error}
    end.

-file("src/gdo/statement.gleam", 153).
-spec execute(statement(), list(gdo@value:param())) -> {ok,
        gdo@result:execution_result()} |
    {error, gdo@error:error()}.
execute(Statement, Params) ->
    exec(Statement, Params).

-file("src/gdo/statement.gleam", 160).
-spec query_all(statement(), list(gdo@value:param())) -> {ok,
        gdo@result:query_result()} |
    {error, gdo@error:error()}.
query_all(Statement, Params) ->
    case validate_params(Statement, Params) of
        {ok, _} ->
            case backend(Statement) of
                {bound, Contract, Statement_state} ->
                    gdo@driver:query_all(Contract, Statement_state, Params);

                unbound ->
                    {ok, gdo@result:empty_query_result()}
            end;

        {error, Error} ->
            {error, Error}
    end.

-file("src/gdo/statement.gleam", 175).
-spec query_one(statement(), list(gdo@value:param())) -> {ok,
        gleam@option:option(gdo@row:row())} |
    {error, gdo@error:error()}.
query_one(Statement, Params) ->
    case query_all(Statement, Params) of
        {ok, Query_result} ->
            {ok, gdo@result:first(Query_result)};

        {error, Error} ->
            {error, Error}
    end.

-file("src/gdo/statement.gleam", 185).
-spec query_all_as(
    statement(),
    list(gdo@value:param()),
    fun((gdo@row:row()) -> {ok, LNT} | {error, gdo@error:error()})
) -> {ok, list(LNT)} | {error, gdo@error:error()}.
query_all_as(Statement, Params, Decoder) ->
    case query_all(Statement, Params) of
        {ok, Query_result} ->
            gleam@list:try_map(
                gdo@result:rows(Query_result),
                fun(Current_row) -> gdo@decode:decode(Current_row, Decoder) end
            );

        {error, Error} ->
            {error, Error}
    end.

-file("src/gdo/statement.gleam", 199).
-spec query_one_as(
    statement(),
    list(gdo@value:param()),
    fun((gdo@row:row()) -> {ok, LNZ} | {error, gdo@error:error()})
) -> {ok, gleam@option:option(LNZ)} | {error, gdo@error:error()}.
query_one_as(Statement, Params, Decoder) ->
    case query_one(Statement, Params) of
        {ok, {some, Current_row}} ->
            case gdo@decode:decode(Current_row, Decoder) of
                {ok, Decoded} ->
                    {ok, {some, Decoded}};

                {error, Error} ->
                    {error, Error}
            end;

        {ok, none} ->
            {ok, none};

        {error, Error@1} ->
            {error, Error@1}
    end.