Skip to main content

src/gdo@driver@sqlite.erl

-module(gdo@driver@sqlite).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gdo/driver/sqlite.gleam").
-export([capabilities/0, contract/0]).

-file("src/gdo/driver/sqlite.gleam", 19).
-spec capabilities() -> list(gdo@driver:capability()).
capabilities() ->
    gdo@driver:capabilities(sqlite).

-file("src/gdo/driver/sqlite.gleam", 334).
-spec sqlite_error_details(sqlight:error_code(), integer()) -> list({binary(),
    binary()}).
sqlite_error_details(Code, Offset) ->
    [{<<"driver"/utf8>>, <<"sqlite"/utf8>>},
        {<<"driver_code"/utf8>>,
            erlang:integer_to_binary(sqlight:error_code_to_int(Code))},
        {<<"error_offset"/utf8>>, erlang:integer_to_binary(Offset)}].

-file("src/gdo/driver/sqlite.gleam", 206).
-spec query_error(sqlight:error()) -> gdo@error:error().
query_error(Sqlight_error) ->
    {sqlight_error, Code, Message, Offset} = Sqlight_error,
    {query_error,
        Message,
        none,
        {some, erlang:integer_to_binary(sqlight:error_code_to_int(Code))},
        sqlite_error_details(Code, Offset)}.

-file("src/gdo/driver/sqlite.gleam", 250).
-spec read_last_insert_id(sqlight:connection()) -> {ok,
        gleam@option:option(integer())} |
    {error, gdo@error:error()}.
read_last_insert_id(Connection) ->
    case sqlight:'query'(
        <<"select last_insert_rowid()"/utf8>>,
        Connection,
        [],
        gleam@dynamic@decode:at(
            [0],
            {decoder, fun gleam@dynamic@decode:decode_int/1}
        )
    ) of
        {ok, [0]} ->
            {ok, none};

        {ok, [Last_insert_id]} ->
            {ok, {some, Last_insert_id}};

        {ok, _} ->
            {error,
                {decode_error,
                    <<"SQLite did not return a last_insert_rowid() value."/utf8>>}};

        {error, Error} ->
            {error, query_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 165).
-spec last_insert_id(gdo@driver:driver_connection_state()) -> gleam@option:option(integer()).
last_insert_id(Connection_state) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    case read_last_insert_id(Connection) of
        {ok, Last_insert_id} ->
            Last_insert_id;

        {error, _} ->
            none
    end.

-file("src/gdo/driver/sqlite.gleam", 216).
-spec transaction_error(sqlight:error()) -> gdo@error:error().
transaction_error(Sqlight_error) ->
    {sqlight_error, _, Message, _} = Sqlight_error,
    {transaction_error, Message}.

-file("src/gdo/driver/sqlite.gleam", 154).
-spec rollback(gdo@driver:driver_connection_state()) -> {ok,
        gdo@driver:driver_connection_state()} |
    {error, gdo@error:error()}.
rollback(Connection_state) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    case sqlight:exec(<<"ROLLBACK"/utf8>>, Connection) of
        {ok, _} ->
            {ok, Connection_state};

        {error, Error} ->
            {error, transaction_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 143).
-spec commit(gdo@driver:driver_connection_state()) -> {ok,
        gdo@driver:driver_connection_state()} |
    {error, gdo@error:error()}.
commit(Connection_state) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    case sqlight:exec(<<"COMMIT"/utf8>>, Connection) of
        {ok, _} ->
            {ok, Connection_state};

        {error, Error} ->
            {error, transaction_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 132).
-spec 'begin'(gdo@driver:driver_connection_state()) -> {ok,
        gdo@driver:driver_connection_state()} |
    {error, gdo@error:error()}.
'begin'(Connection_state) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    case sqlight:exec(<<"BEGIN"/utf8>>, Connection) of
        {ok, _} ->
            {ok, Connection_state};

        {error, Error} ->
            {error, transaction_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 330).
-spec column_name(integer()) -> binary().
column_name(Index) ->
    <<"column_"/utf8, (erlang:integer_to_binary(Index))/binary>>.

-file("src/gdo/driver/sqlite.gleam", 304).
-spec decode_non_null_db_value(gleam@dynamic:dynamic_()) -> {ok,
        gdo@value:db_value()} |
    {error, gdo@error:error()}.
decode_non_null_db_value(Dynamic_value) ->
    case gleam@dynamic@decode:run(
        Dynamic_value,
        {decoder, fun gleam@dynamic@decode:decode_int/1}
    ) of
        {ok, Current_value} ->
            {ok, {int, Current_value}};

        {error, _} ->
            case gleam@dynamic@decode:run(
                Dynamic_value,
                {decoder, fun gleam@dynamic@decode:decode_float/1}
            ) of
                {ok, Current_value@1} ->
                    {ok, {float, Current_value@1}};

                {error, _} ->
                    case gleam@dynamic@decode:run(
                        Dynamic_value,
                        {decoder, fun gleam@dynamic@decode:decode_string/1}
                    ) of
                        {ok, Current_value@2} ->
                            {ok, {string, Current_value@2}};

                        {error, _} ->
                            case gleam@dynamic@decode:run(
                                Dynamic_value,
                                {decoder,
                                    fun gleam@dynamic@decode:decode_bit_array/1}
                            ) of
                                {ok, Current_value@3} ->
                                    {ok, {bytes, Current_value@3}};

                                {error, _} ->
                                    case gleam@dynamic@decode:run(
                                        Dynamic_value,
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_bool/1}
                                    ) of
                                        {ok, Current_value@4} ->
                                            {ok, {bool, Current_value@4}};

                                        {error, _} ->
                                            {error,
                                                {decode_error,
                                                    <<"Unsupported SQLite value type."/utf8>>}}
                                    end
                            end
                    end
            end
    end.

-file("src/gdo/driver/sqlite.gleam", 296).
-spec dynamic_to_db_value(gleam@dynamic:dynamic_()) -> {ok,
        gdo@value:db_value()} |
    {error, gdo@error:error()}.
dynamic_to_db_value(Dynamic_value) ->
    case gleam@dynamic@decode:run(
        Dynamic_value,
        gleam@dynamic@decode:optional(
            {decoder, fun gleam@dynamic@decode:decode_dynamic/1}
        )
    ) of
        {ok, none} ->
            {ok, null};

        {ok, {some, Non_null_value}} ->
            decode_non_null_db_value(Non_null_value);

        {error, _} ->
            {error, {decode_error, <<"Unsupported SQLite value type."/utf8>>}}
    end.

-file("src/gdo/driver/sqlite.gleam", 276).
-spec dynamic_row_to_columns(
    gleam@dynamic:dynamic_(),
    integer(),
    list({binary(), gdo@value:db_value()})
) -> {ok, list({binary(), gdo@value:db_value()})} | {error, gdo@error:error()}.
dynamic_row_to_columns(Current_row, Index, Columns) ->
    case gleam@dynamic@decode:run(
        Current_row,
        gleam@dynamic@decode:at(
            [Index],
            {decoder, fun gleam@dynamic@decode:decode_dynamic/1}
        )
    ) of
        {ok, Dynamic_value} ->
            case dynamic_to_db_value(Dynamic_value) of
                {ok, Db_value} ->
                    dynamic_row_to_columns(
                        Current_row,
                        Index + 1,
                        [{column_name(Index), Db_value} | Columns]
                    );

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

        {error, _} ->
            {ok, lists:reverse(Columns)}
    end.

-file("src/gdo/driver/sqlite.gleam", 269).
-spec decode_sqlite_row(gleam@dynamic:dynamic_()) -> {ok, gdo@row:row()} |
    {error, gdo@error:error()}.
decode_sqlite_row(Current_row) ->
    case dynamic_row_to_columns(Current_row, 0, []) of
        {ok, Columns} ->
            {ok, gdo@row:new(Columns)};

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

-file("src/gdo/driver/sqlite.gleam", 185).
-spec to_sqlight_value(gdo@value:db_value()) -> sqlight:value().
to_sqlight_value(Db_value) ->
    case Db_value of
        null ->
            sqlight_ffi:null();

        {int, Current_value} ->
            sqlight:int(Current_value);

        {float, Current_value@1} ->
            sqlight:float(Current_value@1);

        {bool, Current_value@2} ->
            sqlight:bool(Current_value@2);

        {string, Current_value@3} ->
            sqlight:text(Current_value@3);

        {bytes, Current_value@4} ->
            sqlight_ffi:coerce_blob(Current_value@4)
    end.

-file("src/gdo/driver/sqlite.gleam", 176).
-spec to_sqlight_values(list(gdo@value:param())) -> list(sqlight:value()).
to_sqlight_values(Params) ->
    gleam@list:map(Params, fun(Param) -> case Param of
                {positional, Current_value} ->
                    to_sqlight_value(Current_value);

                {named, _, Current_value@1} ->
                    to_sqlight_value(Current_value@1)
            end end).

-file("src/gdo/driver/sqlite.gleam", 109).
-spec query_all(gdo@driver:driver_statement_state(), list(gdo@value:param())) -> {ok,
        gdo@result:query_result()} |
    {error, gdo@error:error()}.
query_all(Statement_state, Params) ->
    {sqlite_statement_state, Connection, Sql} = Statement_state,
    case sqlight:'query'(
        Sql,
        Connection,
        to_sqlight_values(Params),
        {decoder, fun gleam@dynamic@decode:decode_dynamic/1}
    ) of
        {ok, Rows} ->
            case gleam@list:try_map(Rows, fun decode_sqlite_row/1) of
                {ok, Decoded_rows} ->
                    {ok, gdo@result:query_result(Decoded_rows)};

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

        {error, Error@1} ->
            {error, query_error(Error@1)}
    end.

-file("src/gdo/driver/sqlite.gleam", 235).
-spec read_rows_affected(sqlight:connection()) -> {ok, integer()} |
    {error, gdo@error:error()}.
read_rows_affected(Connection) ->
    case sqlight:'query'(
        <<"select changes()"/utf8>>,
        Connection,
        [],
        gleam@dynamic@decode:at(
            [0],
            {decoder, fun gleam@dynamic@decode:decode_int/1}
        )
    ) of
        {ok, [Rows_affected]} ->
            {ok, Rows_affected};

        {ok, _} ->
            {error,
                {decode_error,
                    <<"SQLite did not return a changes() value."/utf8>>}};

        {error, Error} ->
            {error, query_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 221).
-spec execution_result(sqlight:connection()) -> {ok,
        gdo@result:execution_result()} |
    {error, gdo@error:error()}.
execution_result(Connection) ->
    case read_rows_affected(Connection) of
        {ok, Rows_affected} ->
            case read_last_insert_id(Connection) of
                {ok, Last_insert_id} ->
                    {ok,
                        gdo@result:execution_result(
                            Rows_affected,
                            Last_insert_id
                        )};

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

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

-file("src/gdo/driver/sqlite.gleam", 81).
-spec exec(gdo@driver:driver_statement_state(), list(gdo@value:param())) -> {ok,
        gdo@result:execution_result()} |
    {error, gdo@error:error()}.
exec(Statement_state, Params) ->
    {sqlite_statement_state, Connection, Sql} = Statement_state,
    case to_sqlight_values(Params) of
        [] ->
            case sqlight:exec(Sql, Connection) of
                {ok, _} ->
                    execution_result(Connection);

                {error, Error} ->
                    {error, query_error(Error)}
            end;

        Arguments ->
            case sqlight:'query'(
                Sql,
                Connection,
                Arguments,
                gleam@dynamic@decode:success(nil)
            ) of
                {ok, _} ->
                    execution_result(Connection);

                {error, Error@1} ->
                    {error, query_error(Error@1)}
            end
    end.

-file("src/gdo/driver/sqlite.gleam", 73).
-spec prepare(gdo@driver:driver_connection_state(), binary()) -> {ok,
        gdo@driver:driver_statement_state()} |
    {error, gdo@error:error()}.
prepare(Connection_state, Sql) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    {ok, {sqlite_statement_state, Connection, Sql}}.

-file("src/gdo/driver/sqlite.gleam", 196).
-spec connection_error(sqlight:error()) -> gdo@error:error().
connection_error(Sqlight_error) ->
    {sqlight_error, Code, Message, Offset} = Sqlight_error,
    {connection_error,
        Message,
        none,
        {some, erlang:integer_to_binary(sqlight:error_code_to_int(Code))},
        sqlite_error_details(Code, Offset)}.

-file("src/gdo/driver/sqlite.gleam", 64).
-spec close(gdo@driver:driver_connection_state()) -> {ok, nil} |
    {error, gdo@error:error()}.
close(Connection_state) ->
    {sqlite_connection_state, _, Connection, _} = Connection_state,
    case sqlight:close(Connection) of
        {ok, _} ->
            {ok, nil};

        {error, Error} ->
            {error, connection_error(Error)}
    end.

-file("src/gdo/driver/sqlite.gleam", 37).
-spec connect(gdo@driver:connection_target()) -> {ok,
        gdo@driver:driver_connection_state()} |
    {error, gdo@error:error()}.
connect(Target) ->
    case Target of
        {embedded_database, Path} ->
            case sqlight:open(Path) of
                {ok, Connection} ->
                    {ok, {sqlite_connection_state, Path, Connection, none}};

                {error, Error} ->
                    {error, connection_error(Error)}
            end;

        {server_database, _} ->
            {error,
                {connection_error,
                    <<"SQLite does not support network connection targets."/utf8>>,
                    none,
                    none,
                    [{<<"driver"/utf8>>, <<"sqlite"/utf8>>},
                        {<<"target"/utf8>>, <<"server"/utf8>>}]}}
    end.

-file("src/gdo/driver/sqlite.gleam", 23).
-spec contract() -> gdo@driver:driver_contract().
contract() ->
    {driver_contract,
        fun connect/1,
        fun close/1,
        fun prepare/2,
        fun exec/2,
        fun query_all/2,
        fun 'begin'/1,
        fun commit/1,
        fun rollback/1,
        fun last_insert_id/1}.