-module(rally_runtime@db).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rally_runtime/db.gleam").
-export([open/1, one/1, bool_to_int/1, nullable_text/1, collapse_whitespace/1, 'query'/4, transaction/2, get_timing/0, init_timing/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.
-file("src/rally_runtime/db.gleam", 15).
-spec open(binary()) -> {ok, sqlight:connection()} | {error, sqlight:error()}.
open(Path) ->
gleam@result:'try'(
sqlight:open(Path),
fun(Conn) ->
gleam@result:'try'(
sqlight:exec(<<"PRAGMA journal_mode=WAL;"/utf8>>, Conn),
fun(_) ->
gleam@result:'try'(
sqlight:exec(<<"PRAGMA busy_timeout=5000;"/utf8>>, Conn),
fun(_) ->
gleam@result:'try'(
sqlight:exec(
<<"PRAGMA foreign_keys=ON;"/utf8>>,
Conn
),
fun(_) -> {ok, Conn} end
)
end
)
end
)
end
).
-file("src/rally_runtime/db.gleam", 23).
-spec one(list(AKXW)) -> gleam@option:option(AKXW).
one(Rows) ->
case Rows of
[Row] ->
{some, Row};
_ ->
none
end.
-file("src/rally_runtime/db.gleam", 30).
-spec bool_to_int(boolean()) -> sqlight:value().
bool_to_int(Val) ->
sqlight:int(case Val of
true ->
1;
false ->
0
end).
-file("src/rally_runtime/db.gleam", 37).
-spec nullable_text(gleam@option:option(binary())) -> sqlight:value().
nullable_text(Val) ->
case Val of
{some, S} ->
sqlight:text(S);
none ->
sqlight_ffi:null()
end.
-file("src/rally_runtime/db.gleam", 114).
-spec log_result({ok, list(any())} | {error, sqlight:error()}) -> nil.
log_result(Result) ->
case Result of
{ok, Rows} ->
logging:log(
debug,
<<<<"→ "/utf8,
(erlang:integer_to_binary(erlang:length(Rows)))/binary>>/binary,
" row(s)"/utf8>>
);
{error, Err} ->
logging:log(
warning,
<<"→ DB ERROR: "/utf8, (erlang:element(3, Err))/binary>>
)
end.
-file("src/rally_runtime/db.gleam", 133).
-spec do_collapse_whitespace(binary()) -> binary().
do_collapse_whitespace(Sql) ->
case gleam_stdlib:contains_string(Sql, <<" "/utf8>>) of
true ->
do_collapse_whitespace(
gleam@string:replace(Sql, <<" "/utf8>>, <<" "/utf8>>)
);
_ ->
Sql
end.
-file("src/rally_runtime/db.gleam", 125).
-spec collapse_whitespace(binary()) -> binary().
collapse_whitespace(Sql) ->
_pipe = Sql,
_pipe@1 = gleam@string:replace(_pipe, <<"\n"/utf8>>, <<" "/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"\t"/utf8>>, <<" "/utf8>>),
_pipe@3 = do_collapse_whitespace(_pipe@2),
gleam@string:trim(_pipe@3).
-file("src/rally_runtime/db.gleam", 99).
-spec log_query(binary(), integer(), integer()) -> nil.
log_query(Sql, Param_count, Elapsed_ms) ->
Msg = <<<<<<<<<<(collapse_whitespace(Sql))/binary, " | params: "/utf8>>/binary,
(erlang:integer_to_binary(Param_count))/binary>>/binary,
" ("/utf8>>/binary,
(erlang:integer_to_binary(Elapsed_ms))/binary>>/binary,
"ms)"/utf8>>,
logging:log(debug, Msg).
-file("src/rally_runtime/db.gleam", 47).
?DOC(
" Timed query wrapper. Same signature as sqlight.query but adds debug\n"
" logging with query text, param count, elapsed time, and row count.\n"
" Accumulates timing in the process dictionary for per-request totals.\n"
).
-spec 'query'(
binary(),
sqlight:connection(),
list(sqlight:value()),
gleam@dynamic@decode:decoder(AKYB)
) -> {ok, list(AKYB)} | {error, sqlight:error()}.
'query'(Sql, Conn, Params, Decoder) ->
Start = gleam@time@timestamp:system_time(),
Result = sqlight:'query'(Sql, Conn, Params, Decoder),
Elapsed_ms = begin
_pipe = gleam@time@timestamp:difference(
Start,
gleam@time@timestamp:system_time()
),
gleam@time@duration:to_milliseconds(_pipe)
end,
rally_runtime_db_ffi:add_db_timing(Elapsed_ms),
log_query(Sql, erlang:length(Params), Elapsed_ms),
log_result(Result),
Result.
-file("src/rally_runtime/db.gleam", 66).
-spec transaction(
sqlight:connection(),
fun(() -> {ok, AKYG} | {error, sqlight:error()})
) -> {ok, AKYG} | {error, sqlight:error()}.
transaction(Conn, Body) ->
Id = gleam@int:absolute_value(erlang:unique_integer()),
Savepoint = <<"sp_"/utf8, (erlang:integer_to_binary(Id))/binary>>,
gleam@result:'try'(
sqlight:exec(
<<<<"SAVEPOINT "/utf8, Savepoint/binary>>/binary, ";"/utf8>>,
Conn
),
fun(_) -> case Body() of
{ok, Val} ->
gleam@result:'try'(
sqlight:exec(
<<<<"RELEASE "/utf8, Savepoint/binary>>/binary,
";"/utf8>>,
Conn
),
fun(_) -> {ok, Val} end
);
{error, Err} ->
_ = sqlight:exec(
<<<<"ROLLBACK TO "/utf8, Savepoint/binary>>/binary,
";"/utf8>>,
Conn
),
gleam@result:'try'(
sqlight:exec(
<<<<"RELEASE "/utf8, Savepoint/binary>>/binary,
";"/utf8>>,
Conn
),
fun(_) -> {error, Err} end
)
end end
).
-file("src/rally_runtime/db.gleam", 88).
?DOC(
" Get accumulated DB timing for the current request.\n"
" Returns #(total_milliseconds, query_count).\n"
).
-spec get_timing() -> {integer(), integer()}.
get_timing() ->
rally_runtime_db_ffi:get_db_timing().
-file("src/rally_runtime/db.gleam", 93).
?DOC(" Reset accumulated DB timing. Call at the start of each request/message.\n").
-spec init_timing() -> nil.
init_timing() ->
rally_runtime_db_ffi:init_db_timing().