Current section

Files

Jump to
cigogne src cigogne@internal@database.erl
Raw

src/cigogne@internal@database.erl

-module(cigogne@internal@database).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/cigogne/internal/database.gleam").
-export([init/1, transaction/2, apply_migration/2, apply_migration_no_transaction/2, rollback_migration/2, rollback_migration_no_transaction/2, get_applied_migrations/1, migrations_table_exists/1, apply_cigogne_zero/1, get_error_message/1]).
-export_type([database_data/0, database_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 database_data() :: {database_data, pog:connection(), binary(), binary()}.
-type database_error() :: {env_var_unset, binary()} |
{incorrect_connection_string, binary()} |
{actor_start_error, gleam@otp@actor:start_error()} |
{pog_query_error, pog:query_error()} |
{pog_transaction_error, pog:transaction_error(database_error())}.
-file("src/cigogne/internal/database.gleam", 37).
?DOC(false).
-spec create_migrations_table(binary(), binary()) -> binary().
create_migrations_table(Schema, Migrations_table) ->
<<<<<<<<"CREATE TABLE IF NOT EXISTS "/utf8, Schema/binary>>/binary,
"."/utf8>>/binary,
Migrations_table/binary>>/binary,
"(
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name VARCHAR(255) NOT NULL,
sha256 VARCHAR(64) NOT NULL,
createdAt TIMESTAMP WITHOUT TIME ZONE NOT NULL,
appliedAt TIMESTAMP NOT NULL DEFAULT NOW()
);"/utf8>>.
-file("src/cigogne/internal/database.gleam", 47).
?DOC(false).
-spec query_insert_migration(binary(), binary()) -> binary().
query_insert_migration(Schema, Migrations_table) ->
<<<<<<<<"INSERT INTO "/utf8, Schema/binary>>/binary, "."/utf8>>/binary,
Migrations_table/binary>>/binary,
"(createdAt, name, sha256) VALUES ($1, $2, $3);"/utf8>>.
-file("src/cigogne/internal/database.gleam", 55).
?DOC(false).
-spec query_drop_migration(binary(), binary()) -> binary().
query_drop_migration(Schema, Migrations_table) ->
<<<<<<<<"DELETE FROM "/utf8, Schema/binary>>/binary, "."/utf8>>/binary,
Migrations_table/binary>>/binary,
" WHERE name = $1 AND createdAt = $2;"/utf8>>.
-file("src/cigogne/internal/database.gleam", 63).
?DOC(false).
-spec query_applied_migrations(binary(), binary()) -> binary().
query_applied_migrations(Schema, Migrations_table) ->
<<<<<<<<"SELECT createdAt, name, sha256 FROM "/utf8, Schema/binary>>/binary,
"."/utf8>>/binary,
Migrations_table/binary>>/binary,
" ORDER BY appliedAt ASC;"/utf8>>.
-file("src/cigogne/internal/database.gleam", 107).
?DOC(false).
-spec apply_if_some(LCL, gleam@option:option(LCM), fun((LCL, LCM) -> LCL)) -> LCL.
apply_if_some(Input, Value, Apply_fn) ->
case Value of
{some, V} ->
Apply_fn(Input, V);
none ->
Input
end.
-file("src/cigogne/internal/database.gleam", 118).
?DOC(false).
-spec connection_from_url(binary()) -> {ok, pog:connection()} |
{error, database_error()}.
connection_from_url(Url) ->
Db_process_name = gleam_erlang_ffi:new_name(<<"cigogne"/utf8>>),
_pipe = pog:url_config(Db_process_name, Url),
_pipe@1 = gleam@result:replace_error(
_pipe,
{incorrect_connection_string, Url}
),
_pipe@3 = gleam@result:'try'(_pipe@1, fun(C) -> _pipe@2 = pog:start(C),
gleam@result:map_error(
_pipe@2,
fun(Field@0) -> {actor_start_error, Field@0} end
) end),
gleam@result:map(_pipe@3, fun(Actor) -> erlang:element(3, Actor) end).
-file("src/cigogne/internal/database.gleam", 81).
?DOC(false).
-spec connect(cigogne@config:database_config()) -> {ok, pog:connection()} |
{error, database_error()}.
connect(Config) ->
case Config of
env_var_config ->
_pipe = envoy_ffi:get(<<"DATABASE_URL"/utf8>>),
_pipe@1 = gleam@result:replace_error(
_pipe,
{env_var_unset, <<"DATABASE_URL"/utf8>>}
),
gleam@result:'try'(_pipe@1, fun connection_from_url/1);
{url_db_config, Url} ->
connection_from_url(Url);
{connection_db_config, Connection} ->
{ok, Connection};
{detailed_db_config, Host, User, Password, Port, Name} ->
Procname = gleam_erlang_ffi:new_name(<<"cigogne-db"/utf8>>),
Config@1 = begin
_pipe@2 = pog:default_config(Procname),
_pipe@3 = apply_if_some(_pipe@2, User, fun pog:user/2),
_pipe@4 = pog:password(_pipe@3, Password),
_pipe@5 = apply_if_some(_pipe@4, Host, fun pog:host/2),
_pipe@6 = apply_if_some(_pipe@5, Port, fun pog:port/2),
apply_if_some(_pipe@6, Name, fun pog:database/2)
end,
_pipe@7 = pog:start(Config@1),
_pipe@8 = gleam@result:map_error(
_pipe@7,
fun(Field@0) -> {actor_start_error, Field@0} end
),
gleam@result:map(
_pipe@8,
fun(Actor) -> erlang:element(3, Actor) end
)
end.
-file("src/cigogne/internal/database.gleam", 71).
?DOC(false).
-spec init(cigogne@config:config()) -> {ok, database_data()} |
{error, database_error()}.
init(Config) ->
gleam@result:'try'(
connect(erlang:element(2, Config)),
fun(Connection) ->
{ok,
{database_data,
Connection,
begin
_pipe = erlang:element(3, erlang:element(3, Config)),
gleam@option:unwrap(_pipe, <<"_migrations"/utf8>>)
end,
begin
_pipe@1 = erlang:element(2, erlang:element(3, Config)),
gleam@option:unwrap(_pipe@1, <<"public"/utf8>>)
end}}
end
).
-file("src/cigogne/internal/database.gleam", 235).
?DOC(false).
-spec transaction(
database_data(),
fun((pog:connection()) -> {ok, LDC} | {error, database_error()})
) -> {ok, LDC} | {error, database_error()}.
transaction(Data, Callback) ->
_pipe = begin
pog:transaction(
erlang:element(2, Data),
fun(Transaction) -> Callback(Transaction) end
)
end,
gleam@result:map_error(
_pipe,
fun(Field@0) -> {pog_transaction_error, Field@0} end
).
-file("src/cigogne/internal/database.gleam", 246).
?DOC(false).
-spec insert_migration_query(database_data(), cigogne@migration:migration()) -> pog:'query'(nil).
insert_migration_query(Data, Migration) ->
_pipe = query_insert_migration(
erlang:element(4, Data),
erlang:element(3, Data)
),
_pipe@1 = pog:'query'(_pipe),
_pipe@2 = pog:parameter(
_pipe@1,
pog:timestamp(erlang:element(3, Migration))
),
_pipe@3 = pog:parameter(
_pipe@2,
pog_ffi:coerce(erlang:element(4, Migration))
),
pog:parameter(_pipe@3, pog_ffi:coerce(erlang:element(7, Migration))).
-file("src/cigogne/internal/database.gleam", 165).
?DOC(false).
-spec apply_migration(database_data(), cigogne@migration:migration()) -> {ok,
nil} |
{error, database_error()}.
apply_migration(Data, Migration) ->
_pipe@5 = begin
pog:transaction(
erlang:element(2, Data),
fun(Transaction) ->
_pipe@1 = gleam@list:try_each(
erlang:element(5, Migration),
fun(Q) -> _pipe = pog:'query'(Q),
pog:execute(_pipe, Transaction) end
),
_pipe@4 = gleam@result:'try'(
_pipe@1,
fun(_) -> _pipe@2 = insert_migration_query(Data, Migration),
_pipe@3 = pog:execute(_pipe@2, Transaction),
gleam@result:replace(_pipe@3, nil) end
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {pog_query_error, Field@0} end
)
end
)
end,
gleam@result:map_error(
_pipe@5,
fun(Field@0) -> {pog_transaction_error, Field@0} end
).
-file("src/cigogne/internal/database.gleam", 205).
?DOC(false).
-spec apply_migration_no_transaction(
database_data(),
cigogne@migration:migration()
) -> {ok, nil} | {error, database_error()}.
apply_migration_no_transaction(Data, Migration) ->
_pipe@1 = gleam@list:try_each(
erlang:element(5, Migration),
fun(Q) -> _pipe = pog:'query'(Q),
pog:execute(_pipe, erlang:element(2, Data)) end
),
_pipe@4 = gleam@result:'try'(
_pipe@1,
fun(_) -> _pipe@2 = insert_migration_query(Data, Migration),
_pipe@3 = pog:execute(_pipe@2, erlang:element(2, Data)),
gleam@result:replace(_pipe@3, nil) end
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {pog_query_error, Field@0} end
).
-file("src/cigogne/internal/database.gleam", 257).
?DOC(false).
-spec drop_migration_query(database_data(), cigogne@migration:migration()) -> pog:'query'(nil).
drop_migration_query(Data, Migration) ->
_pipe = query_drop_migration(
erlang:element(4, Data),
erlang:element(3, Data)
),
_pipe@1 = pog:'query'(_pipe),
_pipe@2 = pog:parameter(
_pipe@1,
pog_ffi:coerce(erlang:element(4, Migration))
),
pog:parameter(_pipe@2, pog:timestamp(erlang:element(3, Migration))).
-file("src/cigogne/internal/database.gleam", 185).
?DOC(false).
-spec rollback_migration(database_data(), cigogne@migration:migration()) -> {ok,
nil} |
{error, database_error()}.
rollback_migration(Data, Migration) ->
_pipe@5 = begin
pog:transaction(
erlang:element(2, Data),
fun(Transaction) ->
_pipe@1 = gleam@list:try_each(
erlang:element(6, Migration),
fun(Q) -> _pipe = pog:'query'(Q),
pog:execute(_pipe, Transaction) end
),
_pipe@4 = gleam@result:'try'(
_pipe@1,
fun(_) -> _pipe@2 = drop_migration_query(Data, Migration),
_pipe@3 = pog:execute(_pipe@2, Transaction),
gleam@result:replace(_pipe@3, nil) end
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {pog_query_error, Field@0} end
)
end
)
end,
gleam@result:map_error(
_pipe@5,
fun(Field@0) -> {pog_transaction_error, Field@0} end
).
-file("src/cigogne/internal/database.gleam", 220).
?DOC(false).
-spec rollback_migration_no_transaction(
database_data(),
cigogne@migration:migration()
) -> {ok, nil} | {error, database_error()}.
rollback_migration_no_transaction(Data, Migration) ->
_pipe@1 = gleam@list:try_each(
erlang:element(6, Migration),
fun(Q) -> _pipe = pog:'query'(Q),
pog:execute(_pipe, erlang:element(2, Data)) end
),
_pipe@4 = gleam@result:'try'(
_pipe@1,
fun(_) -> _pipe@2 = drop_migration_query(Data, Migration),
_pipe@3 = pog:execute(_pipe@2, erlang:element(2, Data)),
gleam@result:replace(_pipe@3, nil) end
),
gleam@result:map_error(
_pipe@4,
fun(Field@0) -> {pog_query_error, Field@0} end
).
-file("src/cigogne/internal/database.gleam", 283).
?DOC(false).
-spec db_data_to_migration(
{gleam@time@timestamp:timestamp(), binary(), binary()}
) -> cigogne@migration:migration().
db_data_to_migration(Data) ->
{migration,
<<""/utf8>>,
erlang:element(1, Data),
erlang:element(2, Data),
[],
[],
erlang:element(3, Data)}.
-file("src/cigogne/internal/database.gleam", 267).
?DOC(false).
-spec get_applied_migrations(database_data()) -> {ok,
list(cigogne@migration:migration())} |
{error, database_error()}.
get_applied_migrations(Data) ->
_pipe = query_applied_migrations(
erlang:element(4, Data),
erlang:element(3, Data)
),
_pipe@1 = pog:'query'(_pipe),
_pipe@2 = pog:returning(
_pipe@1,
begin
gleam@dynamic@decode:field(
0,
pog:timestamp_decoder(),
fun(Timestamp) ->
gleam@dynamic@decode:field(
1,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:field(
2,
{decoder,
fun gleam@dynamic@decode:decode_string/1},
fun(Hash) ->
gleam@dynamic@decode:success(
{Timestamp, Name, Hash}
)
end
)
end
)
end
)
end
),
_pipe@3 = pog:execute(_pipe@2, erlang:element(2, Data)),
_pipe@4 = gleam@result:map_error(
_pipe@3,
fun(Field@0) -> {pog_query_error, Field@0} end
),
gleam@result:map(
_pipe@4,
fun(Returned) -> _pipe@5 = erlang:element(3, Returned),
gleam@list:map(_pipe@5, fun db_data_to_migration/1) end
).
-file("src/cigogne/internal/database.gleam", 300).
?DOC(false).
-spec describe_actor_start_error(gleam@otp@actor:start_error()) -> binary().
describe_actor_start_error(Error) ->
case Error of
{init_exited, _} ->
<<"pog actor initialization exited"/utf8>>;
{init_failed, Message} ->
<<"pog actor initialization failed with message "/utf8,
Message/binary>>;
init_timeout ->
<<"Timeout on pog actor initialization"/utf8>>
end.
-file("src/cigogne/internal/database.gleam", 340).
?DOC(false).
-spec describe_decode_error(gleam@dynamic@decode:decode_error()) -> binary().
describe_decode_error(Error) ->
<<<<<<<<<<<<"Expecting : "/utf8, (erlang:element(2, Error))/binary>>/binary,
", Got : "/utf8>>/binary,
(erlang:element(3, Error))/binary>>/binary,
" [at "/utf8>>/binary,
(begin
_pipe = erlang:element(4, Error),
gleam@string:join(_pipe, <<"/"/utf8>>)
end)/binary>>/binary,
"]"/utf8>>.
-file("src/cigogne/internal/database.gleam", 309).
?DOC(false).
-spec describe_query_error(pog:query_error()) -> binary().
describe_query_error(Error) ->
case Error of
connection_unavailable ->
<<"CONNECTION UNAVAILABLE"/utf8>>;
{constraint_violated, Message, _, _} ->
Message;
{postgresql_error, _, _, Message@1} ->
<<"Postgresql error: "/utf8, Message@1/binary>>;
{unexpected_argument_count, Expected, Got} ->
<<<<<<<<"Expected "/utf8,
(erlang:integer_to_binary(Expected))/binary>>/binary,
" arguments, got "/utf8>>/binary,
(erlang:integer_to_binary(Got))/binary>>/binary,
" !"/utf8>>;
{unexpected_argument_type, Expected@1, Got@1} ->
<<<<<<<<"Expected argument of type "/utf8, Expected@1/binary>>/binary,
", got "/utf8>>/binary,
Got@1/binary>>/binary,
" !"/utf8>>;
{unexpected_result_type, Errs} ->
<<"Unexpected result types !\n "/utf8,
(begin
_pipe = gleam@list:map(Errs, fun describe_decode_error/1),
gleam@string:join(_pipe, <<"\n "/utf8>>)
end)/binary>>;
query_timeout ->
<<"Query Timeout"/utf8>>
end.
-file("src/cigogne/internal/database.gleam", 127).
?DOC(false).
-spec migrations_table_exists(database_data()) -> {ok, boolean()} |
{error, database_error()}.
migrations_table_exists(Data) ->
Tables_query = begin
_pipe = pog:'query'(
<<"SELECT table_name, table_schema
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
AND table_name = $1
AND table_schema = $2;"/utf8>>
),
_pipe@1 = pog:parameter(_pipe, pog_ffi:coerce(erlang:element(3, Data))),
_pipe@2 = pog:parameter(
_pipe@1,
pog_ffi:coerce(erlang:element(4, Data))
),
pog:returning(
_pipe@2,
begin
gleam@dynamic@decode:field(
0,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Name) ->
gleam@dynamic@decode:field(
1,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Schema) ->
gleam@dynamic@decode:success({Name, Schema})
end
)
end
)
end
)
end,
Tables_result = begin
_pipe@3 = Tables_query,
pog:execute(_pipe@3, erlang:element(2, Data))
end,
case Tables_result of
{ok, Tables} ->
{ok, erlang:element(2, Tables) > 0};
{error, Db_err} ->
{error, {pog_query_error, Db_err}}
end.
-file("src/cigogne/internal/database.gleam", 150).
?DOC(false).
-spec apply_cigogne_zero(database_data()) -> {ok, nil} |
{error, database_error()}.
apply_cigogne_zero(Data) ->
case migrations_table_exists(Data) of
{ok, true} ->
{ok, nil};
{error, Err} ->
{error, Err};
{ok, false} ->
_pipe = cigogne@migration:create_zero_migration(
<<"CreateMigrationTable"/utf8>>,
[create_migrations_table(
erlang:element(4, Data),
erlang:element(3, Data)
)],
[]
),
apply_migration(Data, _pipe)
end.
-file("src/cigogne/internal/database.gleam", 330).
?DOC(false).
-spec describe_transaction_error(pog:transaction_error(database_error())) -> binary().
describe_transaction_error(Error) ->
case Error of
{transaction_query_error, Suberror} ->
describe_query_error(Suberror);
{transaction_rolled_back, Error@1} ->
<<"Transaction rolled back : "/utf8,
(get_error_message(Error@1))/binary>>
end.
-file("src/cigogne/internal/database.gleam", 289).
?DOC(false).
-spec get_error_message(database_error()) -> binary().
get_error_message(Error) ->
case Error of
{actor_start_error, Error@1} ->
describe_actor_start_error(Error@1);
{env_var_unset, Name} ->
<<<<"Environment variable "/utf8, Name/binary>>/binary,
" is not set"/utf8>>;
{incorrect_connection_string, Conn_string} ->
<<<<"Connection string "/utf8, Conn_string/binary>>/binary,
" is invalid"/utf8>>;
{pog_query_error, Error@2} ->
describe_query_error(Error@2);
{pog_transaction_error, Error@3} ->
describe_transaction_error(Error@3)
end.