Current section
Files
Jump to
Current section
Files
src/migrant@database.erl
-module(migrant@database).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([exec/2, 'query'/4, create_migrations_table/2, filter_applied_migrations/3, apply_migrations/2]).
-file("src/migrant/database.gleam", 16).
-spec exec(sqlight:connection(), binary()) -> {ok, nil} |
{error, migrant@types:error()}.
exec(Db, Sql) ->
_pipe = sqlight:exec(Sql, Db),
_pipe@1 = gleam@result:replace(_pipe, nil),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {database_error, Field@0} end
).
-file("src/migrant/database.gleam", 22).
-spec 'query'(
sqlight:connection(),
binary(),
list(sqlight:value()),
gleam@dynamic@decode:decoder(GBI)
) -> {ok, list(GBI)} | {error, migrant@types:error()}.
'query'(Db, Sql, Args, Decoder) ->
_pipe = sqlight:'query'(Sql, Db, Args, Decoder),
gleam@result:map_error(_pipe, fun(Field@0) -> {database_error, Field@0} end).
-file("src/migrant/database.gleam", 32).
-spec create_migrations_table(
sqlight:connection(),
fun(() -> {ok, nil} | {error, migrant@types:error()})
) -> {ok, nil} | {error, migrant@types:error()}.
create_migrations_table(Db, Next) ->
gleam_stdlib:println(<<"-> Creating migrations table"/utf8>>),
Query = <<"
CREATE TABLE IF NOT EXISTS __migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
"/utf8>>,
case exec(Db, Query) of
{ok, _} ->
Next();
{error, E} ->
{error, E}
end.
-file("src/migrant/database.gleam", 106).
-spec construct_sqlight_error_message(sqlight:error()) -> binary().
construct_sqlight_error_message(Error) ->
Message = <<<<"\""/utf8, (erlang:element(3, Error))/binary>>/binary,
"\""/utf8>>,
Message@1 = case erlang:element(4, Error) of
-1 ->
Message;
_ ->
<<<<Message/binary, " at offset "/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(4, Error)))/binary>>
end,
Code = begin
_pipe = sqlight:error_code_to_int(erlang:element(2, Error)),
erlang:integer_to_binary(_pipe)
end,
<<<<<<Message@1/binary, " (CODE: "/utf8>>/binary, Code/binary>>/binary,
")"/utf8>>.
-file("src/migrant/database.gleam", 97).
-spec print_filter_error_message(sqlight:error()) -> migrant@types:error().
print_filter_error_message(E) ->
Message = <<"-> Failed to query applied migrations: "/utf8,
(construct_sqlight_error_message(E))/binary>>,
gleam_stdlib:println_error(Message),
{database_error, E}.
-file("src/migrant/database.gleam", 120).
-spec pluralise_migration(integer()) -> binary().
pluralise_migration(Count) ->
case Count of
1 ->
<<"migration"/utf8>>;
_ ->
<<"migrations"/utf8>>
end.
-file("src/migrant/database.gleam", 77).
-spec print_count(gleam@dict:dict(binary(), migrant@types:migration())) -> {gleam@dict:dict(binary(), migrant@types:migration()),
integer()}.
print_count(M) ->
Count = maps:size(M),
case Count of
0 ->
gleam_stdlib:println(<<"-> No migrations to apply"/utf8>>),
{maps:new(), 0};
_ ->
gleam_stdlib:println(
<<<<<<<<"-> Found "/utf8,
(erlang:integer_to_binary(maps:size(M)))/binary>>/binary,
" "/utf8>>/binary,
(pluralise_migration(Count))/binary>>/binary,
" to apply"/utf8>>
),
{M, Count}
end.
-file("src/migrant/database.gleam", 53).
-spec filter_applied_migrations(
sqlight:connection(),
gleam@dict:dict(binary(), migrant@types:migration()),
fun((gleam@dict:dict(binary(), migrant@types:migration())) -> {ok, nil} |
{error, migrant@types:error()})
) -> {ok, nil} | {error, migrant@types:error()}.
filter_applied_migrations(Db, Migrations, Next) ->
gleam_stdlib:println(<<"-> Filtering applied migrations"/utf8>>),
Decoder = gleam@dynamic@decode:at(
[0],
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
gleam@result:'try'(
begin
_pipe = <<"SELECT name FROM __migrations ORDER BY id, name ASC;"/utf8>>,
_pipe@1 = sqlight:'query'(_pipe, Db, [], Decoder),
gleam@result:map_error(_pipe@1, fun print_filter_error_message/1)
end,
fun(Applied) -> _pipe@2 = Migrations,
_pipe@3 = gleam@dict:drop(_pipe@2, Applied),
_pipe@4 = print_count(_pipe@3),
(fun(M) ->
gleam@bool:guard(
erlang:element(2, M) =:= 0,
{ok, nil},
fun() -> Next(erlang:element(1, M)) end
)
end)(_pipe@4) end
).
-file("src/migrant/database.gleam", 164).
-spec with_err_message({ok, GEC} | {error, migrant@types:error()}, binary()) -> {ok,
GEC} |
{error, migrant@types:error()}.
with_err_message(E, Msg) ->
_pipe = E,
gleam@result:map_error(
_pipe,
fun(E@1) ->
Message = case E@1 of
{database_error, Sqlight_error} ->
<<<<Msg/binary, ": "/utf8>>/binary,
(construct_sqlight_error_message(Sqlight_error))/binary>>;
_ ->
Msg
end,
gleam_stdlib:println(<<"-> "/utf8, Message/binary>>),
E@1
end
).
-file("src/migrant/database.gleam", 251).
-spec rollback_with_transaction(sqlight:connection(), migrant@types:error()) -> {ok,
nil} |
{error, migrant@types:error()}.
rollback_with_transaction(Db, Err) ->
gleam_stdlib:println(
<<"-> Migration failed, no down query, rolling back transaction"/utf8>>
),
case exec(Db, <<"ROLLBACK;"/utf8>>) of
{ok, _} ->
{error,
{migration_error,
<<"Migration failed and no down query provided"/utf8>>,
Err}};
{error, E} ->
{error, E}
end.
-file("src/migrant/database.gleam", 266).
-spec rollback_with_user_migration(
binary(),
binary(),
sqlight:connection(),
migrant@types:error()
) -> {ok, any()} | {error, migrant@types:error()}.
rollback_with_user_migration(Name, Sql, Db, Err) ->
gleam_stdlib:println(<<"-> Rolling back migration: "/utf8, Name/binary>>),
case exec(Db, Sql) of
{ok, _} ->
gleam_stdlib:println(
<<"-> Rollback migration succeeded: "/utf8, Name/binary>>
),
gleam@result:'try'(
begin
_pipe = Db,
_pipe@1 = exec(_pipe, <<"COMMIT;"/utf8>>),
_pipe@3 = gleam@result:map_error(
_pipe@1,
fun(E) ->
_ = begin
_pipe@2 = Db,
exec(_pipe@2, <<"ROLLBACK;"/utf8>>)
end,
E
end
),
with_err_message(
_pipe@3,
<<"Failed to commit down migration: "/utf8,
Name/binary>>
)
end,
fun(_) ->
gleam_stdlib:println(
<<"-> Rollback complete: "/utf8, Name/binary>>
),
{error,
{migration_error,
<<"Migration failed, but down query succeeded"/utf8>>,
Err}}
end
);
{error, E@1} ->
gleam_stdlib:println(
<<"-> Failed to rollback migration: "/utf8, Name/binary>>
),
gleam_stdlib:println(<<"-> Rolling back transaction"/utf8>>),
gleam@result:'try'(
begin
_pipe@4 = Db,
_pipe@5 = exec(_pipe@4, <<"ROLLBACK;"/utf8>>),
with_err_message(
_pipe@5,
<<"Failed to rollback transaction"/utf8>>
)
end,
fun(_) ->
gleam_stdlib:println(
<<"-> Rollback complete: "/utf8, Name/binary>>
),
{error, E@1}
end
)
end.
-file("src/migrant/database.gleam", 311).
-spec mark_migration_as_applied(
sqlight:connection(),
{binary(), migrant@types:migration()}
) -> {ok, nil} | {error, migrant@types:error()}.
mark_migration_as_applied(Db, Migration_tuple) ->
{Name, _} = Migration_tuple,
Sql = <<"INSERT INTO __migrations (name) VALUES (?) returning id;"/utf8>>,
Decoder = gleam@dynamic@decode:at(
[0],
{decoder, fun gleam@dynamic@decode:decode_int/1}
),
case 'query'(Db, Sql, [sqlight:text(Name)], Decoder) of
{ok, _} ->
{ok, nil};
{error, E} ->
{error, E}
end.
-file("src/migrant/database.gleam", 178).
-spec apply({binary(), migrant@types:migration()}, sqlight:connection()) -> {ok,
nil} |
{error, migrant@types:error()}.
apply(Migration_tuple, Db) ->
{Name, Migration} = Migration_tuple,
gleam@bool:lazy_guard(
gleam@option:is_none(erlang:element(2, Migration)),
fun() ->
gleam_stdlib:println(
<<<<"-> Skipping migration: "/utf8, Name/binary>>/binary,
" no `up` query"/utf8>>
),
{ok, nil}
end,
fun() ->
gleam_stdlib:println(
<<"-> Applying migration: "/utf8, Name/binary>>
),
gleam@result:'try'(
begin
_pipe = Db,
_pipe@1 = exec(_pipe, <<"BEGIN TRANSACTION;"/utf8>>),
with_err_message(
_pipe@1,
<<"Failed to begin transaction"/utf8>>
)
end,
fun(_) ->
Res = begin
gleam@result:'try'(
begin
_pipe@3 = exec(
Db,
begin
_pipe@2 = erlang:element(2, Migration),
gleam@option:unwrap(
_pipe@2,
<<""/utf8>>
)
end
),
with_err_message(
_pipe@3,
<<<<"Failed to apply migration `"/utf8,
Name/binary>>/binary,
"`"/utf8>>
)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@4 = mark_migration_as_applied(
Db,
Migration_tuple
),
with_err_message(
_pipe@4,
<<<<"Failed to mark migration as applied `"/utf8,
Name/binary>>/binary,
"`"/utf8>>
)
end,
fun(_) ->
gleam@result:'try'(
begin
_pipe@5 = Db,
_pipe@6 = exec(
_pipe@5,
<<"COMMIT;"/utf8>>
),
with_err_message(
_pipe@6,
<<"Failed to commit transaction"/utf8>>
)
end,
fun(_) -> {ok, nil} end
)
end
)
end
)
end,
gleam@bool:lazy_guard(
gleam@result:is_ok(Res),
fun() ->
gleam_stdlib:println(
<<"-> Migration applied successfully: "/utf8,
Name/binary>>
),
{ok, nil}
end,
fun() ->
{error, Err} = case Res of
{error, _} -> Res;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
value => _assert_fail,
module => <<"migrant/database"/utf8>>,
function => <<"apply"/utf8>>,
line => 230})
end,
gleam@bool:lazy_guard(
gleam@option:is_none(
erlang:element(3, Migration)
),
fun() -> _pipe@7 = Err,
rollback_with_transaction(Db, _pipe@7) end,
fun() ->
gleam@result:'try'(
begin
_pipe@8 = Err,
_pipe@10 = rollback_with_user_migration(
Name,
begin
_pipe@9 = erlang:element(
3,
Migration
),
gleam@option:unwrap(
_pipe@9,
<<""/utf8>>
)
end,
Db,
_pipe@8
),
with_err_message(
_pipe@10,
<<<<"Failed to rollback migration `"/utf8,
Name/binary>>/binary,
"`"/utf8>>
)
end,
fun(_) -> {ok, nil} end
)
end
)
end
)
end
)
end
).
-file("src/migrant/database.gleam", 148).
-spec run_migration(
list({binary(), migrant@types:migration()}),
sqlight:connection(),
integer()
) -> {ok, integer()} | {error, migrant@types:error()}.
run_migration(Migrations, Db, Count) ->
case Migrations of
[] ->
{ok, Count};
[Migration | Rest] ->
case apply(Migration, Db) of
{ok, _} ->
run_migration(Rest, Db, Count + 1);
{error, E} ->
{error, E}
end
end.
-file("src/migrant/database.gleam", 325).
-spec sort_migrations(gleam@dict:dict(binary(), migrant@types:migration())) -> list({binary(),
migrant@types:migration()}).
sort_migrations(Migrations) ->
_pipe = Migrations,
_pipe@1 = maps:to_list(_pipe),
gleam@list:sort(
_pipe@1,
fun(A, B) ->
{Name_a, _} = A,
{Name_b, _} = B,
gleam@string:compare(Name_a, Name_b)
end
).
-file("src/migrant/database.gleam", 127).
-spec apply_migrations(
sqlight:connection(),
gleam@dict:dict(binary(), migrant@types:migration())
) -> {ok, nil} | {error, migrant@types:error()}.
apply_migrations(Db, Migrations) ->
gleam_stdlib:println(<<"-> Applying migrations"/utf8>>),
Count = begin
_pipe = Migrations,
_pipe@1 = sort_migrations(_pipe),
run_migration(_pipe@1, Db, 0)
end,
case Count of
{ok, Count@1} ->
gleam_stdlib:println(
<<<<"-> Applied "/utf8,
(erlang:integer_to_binary(Count@1))/binary>>/binary,
" migrations"/utf8>>
),
{ok, nil};
{error, E} ->
{error, E}
end.